├── test
├── .npmignore
├── UIProject
│ ├── .npmignore
│ ├── assets
│ │ └── Package1
│ │ │ ├── logo128.png
│ │ │ ├── package.xml
│ │ │ └── Main.xml
│ ├── FairyGUI-threejs-test.fairy
│ └── settings
│ │ └── Publish.json
├── assets
│ └── UI
│ │ ├── Package1.fui
│ │ └── Package1_atlas0.png
├── tsconfig.json
├── template.html
├── src
│ └── index.ts
└── webpack.config.js
├── .vscode
├── settings.json
└── tasks.json
├── .gitignore
├── images
└── 20200610-084916.png
├── src
├── ui
│ ├── GLoader3D.ts
│ ├── Margin.ts
│ ├── GRichTextField.ts
│ ├── GObjectPool.ts
│ ├── PackageItem.ts
│ ├── DragDropManager.ts
│ ├── GTextInput.ts
│ ├── UIConfig.ts
│ ├── UIContentScaler.ts
│ ├── FieldTypes.ts
│ ├── GGraph.ts
│ ├── GImage.ts
│ ├── GMovieClip.ts
│ ├── UIObjectFactory.ts
│ ├── Relations.ts
│ └── GScrollBar.ts
├── core
│ ├── mesh
│ │ ├── MeshFactory.ts
│ │ ├── CompositeMesh.ts
│ │ ├── RectMesh.ts
│ │ ├── RegularPolygonMesh.ts
│ │ └── RoundedRectMesh.ts
│ ├── hittest
│ │ ├── IHitTest.ts
│ │ ├── ShapeHitTest.ts
│ │ └── PixelHitTest.ts
│ ├── text
│ │ ├── BaseFont.ts
│ │ ├── FontManager.ts
│ │ ├── TextFormat.ts
│ │ ├── SelectionShape.ts
│ │ ├── RichTextField.ts
│ │ └── BitmapFont.ts
│ ├── NTexture.ts
│ ├── Shape.ts
│ └── NMaterial.ts
├── utils
│ ├── Color.ts
│ ├── html
│ │ ├── IHtmlPageContext.ts
│ │ ├── HtmlParseOptions.ts
│ │ ├── IHtmlObject.ts
│ │ ├── HtmlPageContext.ts
│ │ ├── HtmlLink.ts
│ │ ├── HtmlImage.ts
│ │ └── HtmlElement.ts
│ ├── Pool.ts
│ ├── ToolSet.ts
│ ├── Rect.ts
│ ├── Timers.ts
│ ├── xml
│ │ ├── XML.ts
│ │ └── XMLUtils.ts
│ ├── ColorMatrix.ts
│ ├── ByteBuffer.ts
│ └── UBBParser.ts
├── tween
│ ├── EaseType.ts
│ ├── TweenValue.ts
│ ├── GTween.ts
│ ├── GPathPoint.ts
│ └── TweenManager.ts
├── gears
│ ├── GearDisplay2.ts
│ ├── GearIcon.ts
│ ├── GearText.ts
│ ├── GearDisplay.ts
│ ├── GearFontSize.ts
│ ├── GearAnimation.ts
│ ├── GearColor.ts
│ ├── GearBase.ts
│ ├── GearLook.ts
│ ├── GearXY.ts
│ └── GearSize.ts
├── action
│ ├── ControllerAction.ts
│ ├── PlayTransitionAction.ts
│ └── ChangePageAction.ts
├── FairyGUI.ts
└── event
│ ├── Event.ts
│ └── EventDispatcher.ts
├── tsconfig.json
├── LICENSE
├── package.json
├── gulpfile.js
└── README.md
/test/.npmignore:
--------------------------------------------------------------------------------
1 | build
--------------------------------------------------------------------------------
/test/UIProject/.npmignore:
--------------------------------------------------------------------------------
1 | .objs
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "liveServer.settings.port": 5501
3 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | .objs
4 | build
5 | package-lock.json
6 |
--------------------------------------------------------------------------------
/images/20200610-084916.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fairygui/FairyGUI-threejs/HEAD/images/20200610-084916.png
--------------------------------------------------------------------------------
/src/ui/GLoader3D.ts:
--------------------------------------------------------------------------------
1 | import { GObject } from "./GObject";
2 |
3 | export class GLoader3D extends GObject
4 | {
5 | }
--------------------------------------------------------------------------------
/test/assets/UI/Package1.fui:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fairygui/FairyGUI-threejs/HEAD/test/assets/UI/Package1.fui
--------------------------------------------------------------------------------
/test/assets/UI/Package1_atlas0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fairygui/FairyGUI-threejs/HEAD/test/assets/UI/Package1_atlas0.png
--------------------------------------------------------------------------------
/test/UIProject/assets/Package1/logo128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fairygui/FairyGUI-threejs/HEAD/test/UIProject/assets/Package1/logo128.png
--------------------------------------------------------------------------------
/src/core/mesh/MeshFactory.ts:
--------------------------------------------------------------------------------
1 | import { VertexBuffer } from "./VertexBuffer";
2 |
3 | export interface IMeshFactory {
4 | onPopulateMesh(vb: VertexBuffer): void;
5 | }
--------------------------------------------------------------------------------
/test/UIProject/FairyGUI-threejs-test.fairy:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/core/hittest/IHitTest.ts:
--------------------------------------------------------------------------------
1 | import { Rect } from "../../utils/Rect";
2 |
3 | export interface IHitTest {
4 | hitTest(contentRect: Rect, x: number, y: number): boolean;
5 | }
--------------------------------------------------------------------------------
/src/utils/Color.ts:
--------------------------------------------------------------------------------
1 | import { Color } from "three";
2 |
3 | export class Color4 extends Color {
4 | public a: number;
5 |
6 | public constructor(rgb?: number, a?: number) {
7 | super(rgb || 0);
8 | this.a = a != null ? a : 1;
9 | }
10 | }
--------------------------------------------------------------------------------
/src/utils/html/IHtmlPageContext.ts:
--------------------------------------------------------------------------------
1 | import { RichTextField } from "../../core/text/RichTextField";
2 | import { HtmlElement } from "./HtmlElement";
3 | import { IHtmlObject } from "./IHtmlObject";
4 |
5 | export interface IHtmlPageContext {
6 | createObject(owner: RichTextField, element: HtmlElement): IHtmlObject;
7 | freeObject(obj: IHtmlObject): void;
8 | }
--------------------------------------------------------------------------------
/test/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "declaration": false,
4 | "target": "es6",
5 | "module": "es6",
6 | "moduleResolution":"Node",
7 | "strict": true,
8 | "strictNullChecks": false
9 | },
10 |
11 | "include": [
12 | "./src/**/*"
13 | ],
14 | "exclude": [
15 | "node_modules"
16 | ]
17 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "declaration": true,
4 | "target": "es6",
5 | "module": "es6",
6 | "moduleResolution":"Node",
7 | "strict": true,
8 | "strictNullChecks": false,
9 | "sourceMap":true
10 | },
11 |
12 | "include": [
13 | "./src/**/*"
14 | ],
15 | "exclude": [
16 | "node_modules"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/test/UIProject/assets/Package1/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/ui/Margin.ts:
--------------------------------------------------------------------------------
1 |
2 | export class Margin {
3 | public left: number = 0;
4 | public right: number = 0;
5 | public top: number = 0;
6 | public bottom: number = 0;
7 |
8 | public copy(source: Margin): void {
9 | this.top = source.top;
10 | this.bottom = source.bottom;
11 | this.left = source.left;
12 | this.right = source.right;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "type": "npm",
6 | "script": "build",
7 | "path": "/",
8 | "group": "build",
9 | "problemMatcher": [],
10 | "label": "build source"
11 | },
12 |
13 | {
14 | "type": "npm",
15 | "script": "test",
16 | "path": "/",
17 | "group": "build",
18 | "problemMatcher": [],
19 | "label": "build test"
20 | }
21 | ]
22 | }
--------------------------------------------------------------------------------
/src/utils/html/HtmlParseOptions.ts:
--------------------------------------------------------------------------------
1 | export class HtmlParseOptions {
2 | public linkUnderline: boolean;
3 | public linkColor: number;
4 | public ignoreWhiteSpace: boolean;
5 |
6 | public static defaultLinkUnderline: boolean = true;
7 | public static defaultLinkColor: number = 0x3A67CC;
8 |
9 | public constructor() {
10 | this.linkUnderline = HtmlParseOptions.defaultLinkUnderline;
11 | this.linkColor = HtmlParseOptions.defaultLinkColor;
12 | }
13 | }
--------------------------------------------------------------------------------
/src/utils/html/IHtmlObject.ts:
--------------------------------------------------------------------------------
1 | import { HtmlElement } from "./HtmlElement";
2 | import { RichTextField } from "../../core/text/RichTextField";
3 | import { DisplayObject } from "../../core/DisplayObject";
4 |
5 | export interface IHtmlObject {
6 | width: number;
7 | height: number;
8 | displayObject: DisplayObject;
9 | element: HtmlElement;
10 |
11 | create(owner: RichTextField, element: HtmlElement):void;
12 | setPosition(x: number, y: number):void;
13 | add():void;
14 | remove():void;
15 | release():void;
16 | dispose():void;
17 | }
--------------------------------------------------------------------------------
/test/UIProject/assets/Package1/Main.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/test/template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%= htmlWebpackPlugin.options.title %>
6 |
7 |
8 | <% for (var css in htmlWebpackPlugin.files.css) { %>
9 |
10 | <% } %>
11 |
12 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/ui/GRichTextField.ts:
--------------------------------------------------------------------------------
1 | import { RichTextField } from "../core/text/RichTextField";
2 | import { defaultParser } from "../utils/UBBParser";
3 | import { GTextField } from "./GTextField";
4 |
5 | export class GRichTextField extends GTextField {
6 | constructor() {
7 | super();
8 | }
9 |
10 | protected createDisplayObject(): void {
11 | this._displayObject = this._textField = new RichTextField();
12 | }
13 |
14 | protected setText() {
15 | let str = this._text;
16 | if (this._template)
17 | str = this.parseTemplate(str);
18 |
19 | this._textField.maxWidth = this.maxWidth;
20 | if (this._ubbEnabled)
21 | this._textField.htmlText = defaultParser.parse(str);
22 | else
23 | this._textField.htmlText = str;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/tween/EaseType.ts:
--------------------------------------------------------------------------------
1 |
2 | export enum EaseType {
3 | Linear = 0,
4 | SineIn = 1,
5 | SineOut = 2,
6 | SineInOut = 3,
7 | QuadIn = 4,
8 | QuadOut = 5,
9 | QuadInOut = 6,
10 | CubicIn = 7,
11 | CubicOut = 8,
12 | CubicInOut = 9,
13 | QuartIn = 10,
14 | QuartOut = 11,
15 | QuartInOut = 12,
16 | QuintIn = 13,
17 | QuintOut = 14,
18 | QuintInOut = 15,
19 | ExpoIn = 16,
20 | ExpoOut = 17,
21 | ExpoInOut = 18,
22 | CircIn = 19,
23 | CircOut = 20,
24 | CircInOut = 21,
25 | ElasticIn = 22,
26 | ElasticOut = 23,
27 | ElasticInOut = 24,
28 | BackIn = 25,
29 | BackOut = 26,
30 | BackInOut = 27,
31 | BounceIn = 28,
32 | BounceOut = 29,
33 | BounceInOut = 30,
34 | Custom = 31,
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/src/core/text/BaseFont.ts:
--------------------------------------------------------------------------------
1 | import { NTexture } from "../NTexture";
2 | import { TextFormat } from "./TextFormat";
3 | import { VertexBuffer } from "../mesh/VertexBuffer";
4 |
5 | export type GlyphInfo = {
6 | width: number;
7 | height: number;
8 | baseline: number;
9 | }
10 |
11 | export interface BaseFont {
12 | name: string;
13 | version: number;
14 | mainTexture: NTexture;
15 | isDynamic?: boolean;
16 | keepCrisp?: boolean;
17 | setFormat(format: TextFormat, fontSizeScale: number): void;
18 | prepareCharacters(text: string): void;
19 | getGlyph(ch: string, ret: GlyphInfo): boolean;
20 | drawGlyph(x: number, y: number, vb: VertexBuffer): number;
21 | drawLine(x: number, y: number, width: number, fontSize: number, type: number, vb: VertexBuffer): number;
22 | getLineHeight(size: number): number;
23 | }
--------------------------------------------------------------------------------
/src/gears/GearDisplay2.ts:
--------------------------------------------------------------------------------
1 | import { GearBase } from "./GearBase";
2 |
3 | export class GearDisplay2 extends GearBase {
4 | public pages: string[] = null;
5 | public condition: number = 0;
6 |
7 | private _visible: number = 0;
8 |
9 | protected init(): void {
10 | this.pages = null;
11 | }
12 |
13 | public apply(): void {
14 | if (this.pages == null || this.pages.length == 0
15 | || this.pages.indexOf(this._controller.selectedPageId) != -1)
16 | this._visible = 1;
17 | else
18 | this._visible = 0;
19 | }
20 |
21 | public evaluate(connected: boolean): boolean {
22 | var v: boolean = this._controller == null || this._visible > 0;
23 | if (this.condition == 0)
24 | v = v && connected;
25 | else
26 | v = v || connected;
27 | return v;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/test/UIProject/settings/Publish.json:
--------------------------------------------------------------------------------
1 | {
2 | "path": "../assets/UI",
3 | "branchPath": "",
4 | "fileExtension": "fui",
5 | "packageCount": 2,
6 | "compressDesc": false,
7 | "binaryFormat": true,
8 | "jpegQuality": 80,
9 | "compressPNG": false,
10 | "codeGeneration": {
11 | "allowGenCode": true,
12 | "codePath": "",
13 | "classNamePrefix": "UI_",
14 | "memberNamePrefix": "m_",
15 | "packageName": null,
16 | "ignoreNoname": true,
17 | "getMemberByName": false,
18 | "codeType": ""
19 | },
20 | "includeHighResolution": 0,
21 | "branchProcessing": 0,
22 | "seperatedAtlasForBranch": false,
23 | "atlasSetting": {
24 | "maxSize": 2048,
25 | "paging": true,
26 | "sizeOption": "pot",
27 | "forceSquare": false,
28 | "allowRotation": false,
29 | "trimImage": true
30 | },
31 | "include2x": false,
32 | "include3x": false,
33 | "include4x": false,
34 | "fileName": "Publish"
35 | }
--------------------------------------------------------------------------------
/src/core/text/FontManager.ts:
--------------------------------------------------------------------------------
1 | import { BaseFont } from "./BaseFont";
2 | import { DynamicFont } from "./DynamicFont";
3 |
4 | export class FontManager {
5 | public static fonts: Record = {};
6 | public static packageFontGetter: (name: string) => BaseFont;
7 |
8 | public static registerFont(font: BaseFont) {
9 | this.fonts[font.name] = font;
10 | }
11 |
12 | public static unregisterFont(font: BaseFont) {
13 | this.fonts[font.name] = undefined;
14 | }
15 |
16 | public static getFont(name: string): BaseFont {
17 | if (this.packageFontGetter && name.startsWith("ui://")) {
18 | let font = this.packageFontGetter(name);
19 | if (font)
20 | return font;
21 | }
22 |
23 | let font = this.fonts[name];
24 | if (!font) {
25 | font = new DynamicFont();
26 | font.name = name;
27 | this.fonts[name] = font;
28 | }
29 |
30 | return font;
31 | }
32 | }
--------------------------------------------------------------------------------
/src/gears/GearIcon.ts:
--------------------------------------------------------------------------------
1 | import { ByteBuffer } from "../utils/ByteBuffer";
2 | import { GearBase } from "./GearBase";
3 |
4 | export class GearIcon extends GearBase {
5 | private _storage: Record;
6 | private _default: string;
7 |
8 | protected init(): void {
9 | this._default = this._owner.icon;
10 | this._storage = {};
11 | }
12 |
13 | protected addStatus(pageId: string, buffer: ByteBuffer): void {
14 | if (!pageId)
15 | this._default = buffer.readS();
16 | else
17 | this._storage[pageId] = buffer.readS();
18 | }
19 |
20 | public apply(): void {
21 | this._owner._gearLocked = true;
22 |
23 | var data: any = this._storage[this._controller.selectedPageId];
24 | if (data !== undefined)
25 | this._owner.icon = data;
26 | else
27 | this._owner.icon = this._default;
28 |
29 | this._owner._gearLocked = false;
30 | }
31 |
32 | public updateState(): void {
33 | this._storage[this._controller.selectedPageId] = this._owner.icon;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/core/hittest/ShapeHitTest.ts:
--------------------------------------------------------------------------------
1 | import { IHitTest } from "./IHitTest";
2 | import { DisplayObject } from "../DisplayObject";
3 | import { Rect } from "../../utils/Rect";
4 | import { Vector2 } from "three";
5 |
6 | var s_vec2: Vector2 = new Vector2();
7 |
8 | export class ShapeHitTest implements IHitTest {
9 | public shape: DisplayObject;
10 |
11 | public constructor(obj: DisplayObject) {
12 | this.shape = obj;
13 | }
14 |
15 | public hitTest(contentRect: Rect, x: number, y: number): boolean {
16 | if (!this.shape.graphics)
17 | return false;
18 |
19 | if (this.shape.parent) {
20 | let p: DisplayObject = (this.shape.parent)["$owner"];
21 | if (p) {
22 | p.transformPoint(x, y, this.shape.obj3D, s_vec2);
23 | x = s_vec2.x;
24 | y = s_vec2.y;
25 | }
26 | }
27 |
28 | let ht: any = this.shape.graphics.meshFactory;
29 | if (!('hitTest' in ht))
30 | return false;
31 |
32 | return (ht).hitTest(this.shape.contentRect, x, y);
33 | }
34 | }
--------------------------------------------------------------------------------
/src/gears/GearText.ts:
--------------------------------------------------------------------------------
1 | import { ByteBuffer } from "../utils/ByteBuffer";
2 | import { GearBase } from "./GearBase";
3 |
4 | export class GearText extends GearBase {
5 | private _storage: Record;
6 | private _default: string;
7 |
8 | protected init(): void {
9 | this._default = this._owner.text;
10 | this._storage = {};
11 | }
12 |
13 | protected addStatus(pageId: string, buffer: ByteBuffer): void {
14 | if (pageId == null)
15 | this._default = buffer.readS();
16 | else
17 | this._storage[pageId] = buffer.readS();
18 | }
19 |
20 | public apply(): void {
21 | this._owner._gearLocked = true;
22 |
23 | var data: any = this._storage[this._controller.selectedPageId];
24 | if (data !== undefined)
25 | this._owner.text = data;
26 | else
27 | this._owner.text = this._default;
28 |
29 | this._owner._gearLocked = false;
30 | }
31 |
32 | public updateState(): void {
33 | this._storage[this._controller.selectedPageId] = this._owner.text;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/gears/GearDisplay.ts:
--------------------------------------------------------------------------------
1 | import { GearBase } from "./GearBase";
2 |
3 | export class GearDisplay extends GearBase {
4 | public pages: string[] = null;
5 |
6 | private _visible: number = 0;
7 | private _displayLockToken: number = 1;
8 |
9 | protected init(): void {
10 | this.pages = null;
11 | }
12 |
13 | public addLock(): number {
14 | this._visible++;
15 | return this._displayLockToken;
16 | }
17 |
18 | public releaseLock(token: number): void {
19 | if (token == this._displayLockToken)
20 | this._visible--;
21 | }
22 |
23 | public get connected(): boolean {
24 | return this._controller == null || this._visible > 0;
25 | }
26 |
27 | public apply(): void {
28 | this._displayLockToken++;
29 | if (this._displayLockToken <= 0)
30 | this._displayLockToken = 1;
31 |
32 | if (this.pages == null || this.pages.length == 0
33 | || this.pages.indexOf(this._controller.selectedPageId) != -1)
34 | this._visible = 1;
35 | else
36 | this._visible = 0;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 FairyGUI
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/gears/GearFontSize.ts:
--------------------------------------------------------------------------------
1 | import { ObjectPropID } from "../ui/FieldTypes";
2 | import { ByteBuffer } from "../utils/ByteBuffer";
3 | import { GearBase } from "./GearBase";
4 |
5 | export class GearFontSize extends GearBase {
6 | private _storage: Record;
7 | private _default: number = 0;
8 |
9 | protected init(): void {
10 | this._default = this._owner.getProp(ObjectPropID.FontSize);
11 | this._storage = {};
12 | }
13 |
14 | protected addStatus(pageId: string, buffer: ByteBuffer): void {
15 | if (!pageId)
16 | this._default = buffer.readInt();
17 | else
18 | this._storage[pageId] = buffer.readInt();
19 | }
20 |
21 | public apply(): void {
22 | this._owner._gearLocked = true;
23 |
24 | var data: any = this._storage[this._controller.selectedPageId];
25 | if (data !== undefined)
26 | this._owner.setProp(ObjectPropID.FontSize, data);
27 | else
28 | this._owner.setProp(ObjectPropID.FontSize, this._default);
29 |
30 | this._owner._gearLocked = false;
31 | }
32 |
33 | public updateState(): void {
34 | this._storage[this._controller.selectedPageId] = this._owner.getProp(ObjectPropID.FontSize);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/utils/Pool.ts:
--------------------------------------------------------------------------------
1 | export class Pool
2 | {
3 | pool: Array = [];
4 | _init: (arg0: T, ...argArray: any[]) => void;
5 | _reset: (arg0: T) => void;
6 | _ct: new () => T;
7 |
8 | public constructor(type: new () => T, init?: (arg0: T) => void, reset?: (arg0: T) => void) {
9 | this._init = init;
10 | this._reset = reset;
11 | this._ct = type;
12 | }
13 |
14 | public borrow(...argArray: any[]): T {
15 | let ret: T;
16 | if (this.pool.length > 0)
17 | ret = this.pool.pop();
18 | else
19 | ret = new this._ct();
20 |
21 | if (this._init)
22 | this._init(ret, ...argArray);
23 |
24 | return ret;
25 | }
26 |
27 | public returns(element: T | Array) {
28 | if (Array.isArray(element)) {
29 | let count = element.length;
30 | for (let i = 0; i < count; i++) {
31 | let element2 = element[i];
32 | if (this._reset)
33 | this._reset(element2);
34 | this.pool.push(element2);
35 | }
36 | element.length = 0;
37 | }
38 | else {
39 | if (this._reset)
40 | this._reset(element);
41 | this.pool.push(element);
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/src/action/ControllerAction.ts:
--------------------------------------------------------------------------------
1 |
2 | import { Controller } from "../ui/Controller";
3 | import { ByteBuffer } from "../utils/ByteBuffer";
4 |
5 | export class ControllerAction {
6 | public fromPage: Array;
7 | public toPage: Array;
8 |
9 | constructor() {
10 | }
11 |
12 | public run(controller: Controller, prevPage: string, curPage: string): void {
13 | if ((!this.fromPage || this.fromPage.length == 0 || this.fromPage.indexOf(prevPage) != -1)
14 | && (!this.toPage || this.toPage.length == 0 || this.toPage.indexOf(curPage) != -1))
15 | this.enter(controller);
16 | else
17 | this.leave(controller);
18 | }
19 |
20 | protected enter(controller: Controller): void {
21 |
22 | }
23 |
24 | protected leave(controller: Controller): void {
25 |
26 | }
27 |
28 | public setup(buffer: ByteBuffer): void {
29 | var cnt: number;
30 | var i: number;
31 |
32 | cnt = buffer.readShort();
33 | this.fromPage = [];
34 | for (i = 0; i < cnt; i++)
35 | this.fromPage[i] = buffer.readS();
36 |
37 | cnt = buffer.readShort();
38 | this.toPage = [];
39 | for (i = 0; i < cnt; i++)
40 | this.toPage[i] = buffer.readS();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/ui/GObjectPool.ts:
--------------------------------------------------------------------------------
1 | import { GObject } from "./GObject";
2 | import { UIPackage } from "./UIPackage";
3 |
4 | export class GObjectPool {
5 | private _pool: Record>;
6 | private _count: number = 0;
7 |
8 | constructor() {
9 | this._pool = {};
10 | }
11 |
12 | public clear(): void {
13 | for (var i1 in this._pool) {
14 | var arr: Array = this._pool[i1];
15 | arr.forEach(obj => obj.dispose());
16 | }
17 | this._pool = {};
18 | this._count = 0;
19 | }
20 |
21 | public get count(): number {
22 | return this._count;
23 | }
24 |
25 | public getObject(url: string): GObject {
26 | url = UIPackage.normalizeURL(url);
27 | if (url == null)
28 | return null;
29 |
30 | var arr: Array = this._pool[url];
31 | if (arr && arr.length > 0) {
32 | this._count--;
33 | return arr.shift();
34 | }
35 |
36 | return UIPackage.createObjectFromURL(url);
37 | }
38 |
39 | public returnObject(obj: GObject): void {
40 | var url: string = obj.resourceURL;
41 | if (!url)
42 | return;
43 |
44 | var arr: Array = this._pool[url];
45 | if (!arr) {
46 | arr = [];
47 | this._pool[url] = arr;
48 | }
49 |
50 | this._count++;
51 | arr.push(obj);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/utils/html/HtmlPageContext.ts:
--------------------------------------------------------------------------------
1 | import { RichTextField } from "../../core/text/RichTextField";
2 | import { Pool } from "../Pool";
3 | import { HtmlElement, HtmlElementType } from "./HtmlElement";
4 | import { HtmlImage } from "./HtmlImage";
5 | import { HtmlLink } from "./HtmlLink";
6 | import { IHtmlObject } from "./IHtmlObject";
7 | import { IHtmlPageContext } from "./IHtmlPageContext";
8 |
9 | export class HtmlPageContext implements IHtmlPageContext {
10 | private _imagePool: Pool;
11 | private _linkPool: Pool;
12 |
13 | public constructor() {
14 | this._imagePool = new Pool(HtmlImage);
15 | this._linkPool = new Pool(HtmlLink);
16 | }
17 |
18 | public createObject(owner: RichTextField, element: HtmlElement): IHtmlObject {
19 | let ret: IHtmlObject = null;
20 | if (element.type == HtmlElementType.Image)
21 | ret = this._imagePool.borrow();
22 | else if (element.type == HtmlElementType.Link)
23 | ret = this._linkPool.borrow();
24 |
25 | if (ret)
26 | ret.create(owner, element);
27 |
28 | return ret;
29 | }
30 |
31 | public freeObject(obj: IHtmlObject): void {
32 | obj.release();
33 | if (obj instanceof HtmlImage)
34 | this._imagePool.returns(obj);
35 | else if (obj instanceof HtmlLink)
36 | this._linkPool.returns(obj);
37 | }
38 | }
39 |
40 | export var defaultContext: IHtmlPageContext = new HtmlPageContext();
41 |
--------------------------------------------------------------------------------
/src/action/PlayTransitionAction.ts:
--------------------------------------------------------------------------------
1 | import { ControllerAction } from "./ControllerAction";
2 | import { Controller } from "../ui/Controller";
3 | import { Transition } from "../ui/Transition";
4 | import { ByteBuffer } from "../utils/ByteBuffer";
5 |
6 | export class PlayTransitionAction extends ControllerAction {
7 | public transitionName: string;
8 | public playTimes: number = 1;
9 | public delay: number = 0;
10 | public stopOnExit: boolean;
11 |
12 | private _currentTransition: Transition;
13 |
14 | constructor() {
15 | super();
16 | }
17 |
18 | protected enter(controller: Controller): void {
19 | var trans: Transition = controller.parent.getTransition(this.transitionName);
20 | if (trans) {
21 | if (this._currentTransition && this._currentTransition.playing)
22 | trans.changePlayTimes(this.playTimes);
23 | else
24 | trans.play(null, this.playTimes, this.delay);
25 | this._currentTransition = trans;
26 | }
27 | }
28 |
29 | protected leave(controller: Controller): void {
30 | if (this.stopOnExit && this._currentTransition) {
31 | this._currentTransition.stop();
32 | this._currentTransition = null;
33 | }
34 | }
35 |
36 | public setup(buffer: ByteBuffer): void {
37 | super.setup(buffer);
38 |
39 | this.transitionName = buffer.readS();
40 | this.playTimes = buffer.readInt();
41 | this.delay = buffer.readFloat();
42 | this.stopOnExit = buffer.readBool();
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/action/ChangePageAction.ts:
--------------------------------------------------------------------------------
1 | import { GComponent } from "../ui/GComponent";
2 | import { Controller } from "../ui/Controller";
3 | import { ByteBuffer } from "../utils/ByteBuffer";
4 | import { ControllerAction } from "./ControllerAction";
5 |
6 | export class ChangePageAction extends ControllerAction {
7 | public objectId: string;
8 | public controllerName: string;
9 | public targetPage: string;
10 |
11 | constructor() {
12 | super();
13 | }
14 |
15 | protected enter(controller: Controller): void {
16 | if (!this.controllerName)
17 | return;
18 |
19 | var gcom: GComponent;
20 | if (this.objectId)
21 | gcom = controller.parent.getChildById(this.objectId);
22 | else
23 | gcom = controller.parent;
24 | if (gcom) {
25 | var cc: Controller = gcom.getController(this.controllerName);
26 | if (cc && cc != controller && !cc.changing) {
27 | if (this.targetPage == "~1") {
28 | if (controller.selectedIndex < cc.pageCount)
29 | cc.selectedIndex = controller.selectedIndex;
30 | }
31 | else if (this.targetPage == "~2")
32 | cc.selectedPage = controller.selectedPage;
33 | else
34 | cc.selectedPageId = this.targetPage;
35 | }
36 | }
37 | }
38 |
39 | public setup(buffer: ByteBuffer): void {
40 | super.setup(buffer);
41 |
42 | this.objectId = buffer.readS();
43 | this.controllerName = buffer.readS();
44 | this.targetPage = buffer.readS();
45 | }
46 | }
--------------------------------------------------------------------------------
/src/tween/TweenValue.ts:
--------------------------------------------------------------------------------
1 |
2 | export class TweenValue {
3 | public x: number;
4 | public y: number;
5 | public z: number;
6 | public w: number;
7 |
8 | public constructor() {
9 | this.x = this.y = this.z = this.w = 0;
10 | }
11 |
12 | public get color(): number {
13 | return (this.w << 24) + (this.x << 16) + (this.y << 8) + this.z;
14 | }
15 |
16 | public set color(value: number) {
17 | this.x = (value & 0xFF0000) >> 16;
18 | this.y = (value & 0x00FF00) >> 8;
19 | this.z = (value & 0x0000FF);
20 | this.w = (value & 0xFF000000) >> 24;
21 | }
22 |
23 | public getField(index: number): number {
24 | switch (index) {
25 | case 0:
26 | return this.x;
27 | case 1:
28 | return this.y;
29 | case 2:
30 | return this.z;
31 | case 3:
32 | return this.w;
33 | default:
34 | throw new Error("Index out of bounds: " + index);
35 | }
36 | }
37 |
38 | public setField(index: number, value: number): void {
39 | switch (index) {
40 | case 0:
41 | this.x = value;
42 | break;
43 | case 1:
44 | this.y = value;
45 | break;
46 | case 2:
47 | this.z = value;
48 | break;
49 | case 3:
50 | this.w = value;
51 | break;
52 | default:
53 | throw new Error("Index out of bounds: " + index);
54 | }
55 | }
56 |
57 | public setZero(): void {
58 | this.x = this.y = this.z = this.w = 0;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fairygui-three",
3 | "version": "1.0.0",
4 | "description": "A GUI Editor & framework for three.js",
5 | "main": "dist/fairygui.js",
6 | "module": "dist/fairygui.module.js",
7 | "types": "dist/fairygui.d.ts",
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/fairygui/FairyGUI-threejs"
11 | },
12 | "files": [
13 | "dist",
14 | "test"
15 | ],
16 | "scripts": {
17 | "build": "gulp build",
18 | "test": "webpack --config test/webpack.config.js"
19 | },
20 | "keywords": [
21 | "three",
22 | "three.js",
23 | "fairygui",
24 | "fgui",
25 | "gui"
26 | ],
27 | "author": "guzhu",
28 | "license": "MIT",
29 | "homepage": "https://fairygui.com/",
30 | "dependencies": {
31 | "@types/node": "^14.0.1"
32 | },
33 | "devDependencies": {
34 | "three": "^0.154.0",
35 | "@types/three": "^0.154.0",
36 | "@types/node": "^14.0.1",
37 | "clean-webpack-plugin": "^3.0.0",
38 | "copy-webpack-plugin": "^5.1.1",
39 | "css-loader": "^3.5.3",
40 | "csv-loader": "^3.0.3",
41 | "expose-loader": "^0.7.5",
42 | "file-loader": "^6.0.0",
43 | "gulp": "^4.0.2",
44 | "gulp-clean": "^0.4.0",
45 | "gulp-concat": "^2.6.1",
46 | "gulp-minify": "^3.1.0",
47 | "gulp-rename": "^2.0.0",
48 | "gulp-replace": "^1.1.3",
49 | "gulp-typescript": "^6.0.0-alpha.1",
50 | "gulp-uglify": "^3.0.2",
51 | "gulp-uglify-es": "^2.0.0",
52 | "html-webpack-plugin": "^4.3.0",
53 | "rollup": "^3.29.4",
54 | "style-loader": "^1.2.1",
55 | "terser-webpack-plugin": "^3.0.3",
56 | "ts-loader": "^8.3.0",
57 | "webpack": "^4.43.0",
58 | "webpack-cli": "^3.3.11",
59 | "webpack-dev-server": "^3.11.0",
60 | "xml-loader": "^1.2.1",
61 | "dts-bundle": "^0.7.3"
62 | }
63 | }
--------------------------------------------------------------------------------
/src/core/text/TextFormat.ts:
--------------------------------------------------------------------------------
1 | import { AlignType } from "../../ui/FieldTypes";
2 | import { Vector2 } from "three";
3 |
4 | export class TextFormat {
5 | public size: number = 0;
6 | public font: string;
7 | public color: number = 0;
8 | public lineSpacing: number = 0;
9 | public letterSpacing: number = 0;
10 | public bold: boolean;
11 | public underline: boolean;
12 | public italic: boolean;
13 | public strikethrough: boolean;
14 |
15 | public align: AlignType;
16 | public outline: number = 0;
17 | public outlineColor: number = 0;
18 | public shadowOffset: Vector2 = new Vector2();
19 | public shadowColor: number = 0;
20 |
21 | /**@internal */
22 | _colorChanged: boolean;
23 |
24 | public copy(source: TextFormat): void {
25 | this.size = source.size;
26 | this.font = source.font;
27 | this.color = source.color;
28 | this.lineSpacing = source.lineSpacing;
29 | this.letterSpacing = source.letterSpacing;
30 | this.bold = source.bold;
31 | this.underline = source.underline;
32 | this.italic = source.italic;
33 | this.strikethrough = source.strikethrough;
34 | this.align = source.align;
35 | this.outline = source.outline;
36 | this.outlineColor = source.outlineColor;
37 | this.shadowOffset.copy(source.shadowOffset);
38 | this.shadowColor = source.shadowColor;
39 | }
40 |
41 | public equalStyle(aFormat: TextFormat): boolean {
42 | return this.size == aFormat.size && this.color == aFormat.color
43 | && this.bold == aFormat.bold && this.underline == aFormat.underline
44 | && this.italic == aFormat.italic
45 | && this.strikethrough == aFormat.strikethrough
46 | && this.align == aFormat.align;
47 | }
48 | }
--------------------------------------------------------------------------------
/src/core/mesh/CompositeMesh.ts:
--------------------------------------------------------------------------------
1 | import { IMeshFactory } from "./MeshFactory";
2 | import { IHitTest } from "../hittest/IHitTest";
3 | import { VertexBuffer } from "./VertexBuffer";
4 | import { Rect } from "../../utils/Rect";
5 |
6 | export class CompositeMesh implements IMeshFactory, IHitTest {
7 | public readonly elements: Array;
8 |
9 | public activeIndex: number;
10 |
11 | public constructor() {
12 | this.elements = [];
13 | this.activeIndex = -1;
14 | }
15 |
16 | public onPopulateMesh(vb: VertexBuffer) {
17 | let cnt = this.elements.length;
18 | if (cnt == 1)
19 | this.elements[0].onPopulateMesh(vb);
20 | else {
21 | let vb2: VertexBuffer = VertexBuffer.begin(vb);
22 |
23 | for (let i = 0; i < cnt; i++) {
24 | if (this.activeIndex == -1 || i == this.activeIndex) {
25 | vb2.clear();
26 | this.elements[i].onPopulateMesh(vb2);
27 | vb.append(vb2);
28 | }
29 | }
30 |
31 | vb2.end();
32 | }
33 | }
34 |
35 | public hitTest(contentRect: Rect, x: number, y: number): boolean {
36 | if (!contentRect.contains(x, y))
37 | return false;
38 |
39 | let flag: boolean = false;
40 | let cnt = this.elements.length;
41 | for (let i = 0; i < cnt; i++) {
42 | if (this.activeIndex == -1 || i == this.activeIndex) {
43 | let ht = this.elements[i];
44 | if ('hitTest' in ht) {
45 | if ((ht).hitTest(contentRect, x, y))
46 | return true;
47 | }
48 | else
49 | flag = true;
50 | }
51 | }
52 |
53 | return flag;
54 | }
55 | }
--------------------------------------------------------------------------------
/src/gears/GearAnimation.ts:
--------------------------------------------------------------------------------
1 | import { GearBase } from "./GearBase";
2 | import { ByteBuffer } from "../utils/ByteBuffer";
3 | import { ObjectPropID } from "../ui/FieldTypes";
4 |
5 | export class GearAnimation extends GearBase {
6 | private _storage: Record;
7 | private _default: GearAnimationValue;
8 |
9 | protected init(): void {
10 | this._default = {
11 | playing: this._owner.getProp(ObjectPropID.Playing),
12 | frame: this._owner.getProp(ObjectPropID.Frame)
13 | };
14 | this._storage = {};
15 | }
16 |
17 | protected addStatus(pageId: string, buffer: ByteBuffer): void {
18 | var gv: GearAnimationValue;
19 | if (!pageId)
20 | gv = this._default;
21 | else {
22 | gv = {};
23 | this._storage[pageId] = gv;
24 | }
25 | gv.playing = buffer.readBool();
26 | gv.frame = buffer.readInt();
27 | }
28 |
29 | public apply(): void {
30 | this._owner._gearLocked = true;
31 |
32 | var gv: GearAnimationValue = this._storage[this._controller.selectedPageId] || this._default;
33 | this._owner.setProp(ObjectPropID.Playing, gv.playing);
34 | this._owner.setProp(ObjectPropID.Frame, gv.frame);
35 |
36 | this._owner._gearLocked = false;
37 | }
38 |
39 | public updateState(): void {
40 | var gv: GearAnimationValue = this._storage[this._controller.selectedPageId];
41 | if (!gv) {
42 | gv = {};
43 | this._storage[this._controller.selectedPageId] = gv;
44 | }
45 |
46 | gv.playing = this._owner.getProp(ObjectPropID.Playing);
47 | gv.frame = this._owner.getProp(ObjectPropID.Frame);
48 | }
49 | }
50 |
51 | interface GearAnimationValue {
52 | playing?: boolean;
53 | frame?: number;
54 | }
55 |
56 |
--------------------------------------------------------------------------------
/src/gears/GearColor.ts:
--------------------------------------------------------------------------------
1 | import { ObjectPropID } from "../ui/FieldTypes";
2 | import { ByteBuffer } from "../utils/ByteBuffer";
3 | import { GearBase } from "./GearBase";
4 |
5 | export class GearColor extends GearBase {
6 | private _storage: Record;
7 | private _default: GearColorValue;
8 |
9 | protected init(): void {
10 | this._default = {
11 | color: this._owner.getProp(ObjectPropID.Color),
12 | strokeColor: this._owner.getProp(ObjectPropID.OutlineColor)
13 | };
14 | this._storage = {};
15 | }
16 |
17 | protected addStatus(pageId: string, buffer: ByteBuffer): void {
18 | var gv: GearColorValue;
19 | if (!pageId)
20 | gv = this._default;
21 | else {
22 | gv = {};
23 | this._storage[pageId] = gv;
24 | }
25 |
26 | gv.color = buffer.readColor();
27 | gv.strokeColor = buffer.readColor();
28 | }
29 |
30 | public apply(): void {
31 | this._owner._gearLocked = true;
32 |
33 | var gv: GearColorValue = this._storage[this._controller.selectedPageId] || this._default;
34 | this._owner.setProp(ObjectPropID.Color, gv.color);
35 | this._owner.setProp(ObjectPropID.OutlineColor, gv.strokeColor);
36 |
37 | this._owner._gearLocked = false;
38 | }
39 |
40 | public updateState(): void {
41 | var gv: GearColorValue = this._storage[this._controller.selectedPageId];
42 | if (!gv) {
43 | gv = {};
44 | this._storage[this._controller.selectedPageId] = gv;
45 | }
46 |
47 | gv.color = this._owner.getProp(ObjectPropID.Color);
48 | gv.strokeColor = this._owner.getProp(ObjectPropID.OutlineColor);
49 | }
50 | }
51 |
52 | interface GearColorValue {
53 | color?: number;
54 | strokeColor?: number;
55 | }
56 |
--------------------------------------------------------------------------------
/src/core/hittest/PixelHitTest.ts:
--------------------------------------------------------------------------------
1 | import { ByteBuffer } from "../../utils/ByteBuffer";
2 | import { Rect } from "../../utils/Rect";
3 | import { IHitTest } from "./IHitTest";
4 |
5 | export class PixelHitTestData {
6 | public pixelWidth: number;
7 | public scale: number;
8 | public pixels: Uint8Array;
9 |
10 | public load(ba: ByteBuffer) {
11 | ba.readInt();
12 | this.pixelWidth = ba.readInt();
13 | this.scale = 1.0 / ba.readByte();
14 | let len = ba.readInt();
15 | this.pixels = new Uint8Array(ba.data, ba.pos, len);
16 | ba.skip(len);
17 | }
18 | }
19 |
20 | export class PixelHitTest implements IHitTest {
21 | public offsetX: number;
22 | public offsetY: number;
23 | public sourceWidth: number;
24 | public sourceHeight: number;
25 |
26 | private _data: PixelHitTestData;
27 |
28 | public constructor(data: PixelHitTestData, offsetX: number, offsetY: number, sourceWidth: number, sourceHeight: number) {
29 | this._data = data;
30 | this.offsetX = offsetX;
31 | this.offsetY = offsetY;
32 | this.sourceWidth = sourceWidth;
33 | this.sourceHeight = sourceHeight;
34 | }
35 |
36 | public hitTest(contentRect: Rect, x: number, y: number): boolean {
37 | if (!contentRect.contains(x, y))
38 | return false;
39 |
40 | let data = this._data;
41 |
42 | x = Math.floor((x * this.sourceWidth / contentRect.width - this.offsetX) * data.scale);
43 | y = Math.floor((y * this.sourceHeight / contentRect.height - this.offsetY) * data.scale);
44 | if (x < 0 || y < 0 || x >= data.pixelWidth)
45 | return false;
46 |
47 | let pos: number = y * data.pixelWidth + x;
48 | let pos2: number = Math.floor(pos / 8);
49 | let pos3: number = pos % 8;
50 |
51 | if (pos2 >= 0 && pos2 < data.pixels.length)
52 | return ((data.pixels[pos2] >> pos3) & 0x1) > 0;
53 | else
54 | return false;
55 | }
56 | }
--------------------------------------------------------------------------------
/test/src/index.ts:
--------------------------------------------------------------------------------
1 | import * as THREE from "three";
2 | import * as fgui from "../../build/FairyGUI";
3 |
4 | export class Threescene {
5 | private scene: THREE.Scene
6 | private renderer: THREE.WebGLRenderer;
7 |
8 | private _mainMenu: fgui.GComponent;
9 |
10 | constructor() {
11 | this.init();
12 | }
13 |
14 | private init() {
15 | this.renderer = new THREE.WebGLRenderer();//{ antialias: true }
16 | this.renderer.setClearColor(0X222222);
17 | this.renderer.sortObjects = false;
18 | this.renderer.setPixelRatio(window.devicePixelRatio);
19 | this.renderer.setSize(window.innerWidth, window.innerHeight);
20 | this.renderer.localClippingEnabled = true;
21 | document.body.appendChild(this.renderer.domElement);
22 | window.addEventListener('resize', () => {
23 | this.renderer.setSize(window.innerWidth, window.innerHeight)
24 | }, false);
25 |
26 | this.scene = new THREE.Scene();
27 |
28 | fgui.Stage.init(this.renderer);
29 | fgui.Stage.scene = this.scene;
30 |
31 | fgui.UIContentScaler.scaleWithScreenSize(1136, 640, fgui.ScreenMatchMode.MatchWidthOrHeight);
32 |
33 | // let listener: THREE.AudioListener = new THREE.AudioListener();
34 | // fgui.Stage.camera.add(listener);
35 | // fgui.Stage.audioListener = listener;
36 |
37 | fgui.UIPackage.loadPackage("assets/UI/Package1").then(this.start.bind(this));
38 |
39 | this.animate();
40 | }
41 |
42 | private start() {
43 | this._mainMenu = fgui.UIPackage.createObject("Package1", "Main").asCom;
44 | this._mainMenu.makeFullScreen();
45 | fgui.GRoot.inst.addChild(this._mainMenu);
46 | }
47 |
48 | private render() {
49 | fgui.Stage.update();
50 | this.renderer.render(this.scene, fgui.Stage.camera);
51 | }
52 |
53 | private animate = () => {
54 | requestAnimationFrame(this.animate)
55 | this.render()
56 | }
57 | }
58 |
59 | new Threescene();
--------------------------------------------------------------------------------
/src/core/text/SelectionShape.ts:
--------------------------------------------------------------------------------
1 | import { Vector2 } from "three";
2 | import { Rect } from "../../utils/Rect";
3 | import { DisplayObject } from "../DisplayObject";
4 | import { IMeshFactory } from "../mesh/MeshFactory";
5 | import { VertexBuffer } from "../mesh/VertexBuffer";
6 | import { NGraphics } from "../NGraphics";
7 | import { EmptyTexture } from "../NTexture";
8 | import { HitTestContext } from "../Stage";
9 |
10 | var s_rect: Rect = new Rect();
11 |
12 | export class SelectionShape extends DisplayObject implements IMeshFactory {
13 | public readonly rects: Array;
14 |
15 | public constructor() {
16 | super();
17 |
18 | this.rects = new Array();
19 |
20 | this._graphics = new NGraphics(this._obj3D);
21 | this._graphics.texture = EmptyTexture;
22 | }
23 |
24 | public refresh() {
25 | let count = this.rects.length;
26 | if (count > 0) {
27 | s_rect.copy(this.rects[0]);
28 | for (let i = 1; i < count; i++)
29 | s_rect.union(this.rects[i]);
30 | this.setSize(s_rect.xMax, s_rect.yMax);
31 | }
32 | else
33 | this.setSize(0, 0);
34 | this.graphics.setMeshDirty();
35 | }
36 |
37 | public clear() {
38 | this.rects.length = 0;
39 | this.graphics.setMeshDirty();
40 | }
41 |
42 | public onPopulateMesh(vb: VertexBuffer) {
43 | let count = this.rects.length;
44 | if (count == 0)
45 | return;
46 |
47 | for (let i = 0; i < count; i++)
48 | vb.addQuad(this.rects[i]);
49 | vb.addTriangles();
50 | }
51 |
52 | protected hitTest(context: HitTestContext): DisplayObject {
53 | let pt: Vector2 = context.getLocal(this);
54 |
55 | if (this._contentRect.contains(pt)) {
56 | let count = this.rects.length;
57 | for (let i = 0; i < count; i++) {
58 | if (this.rects[i].contains(pt))
59 | return this;
60 | }
61 | }
62 |
63 | return null;
64 | }
65 | }
--------------------------------------------------------------------------------
/src/utils/ToolSet.ts:
--------------------------------------------------------------------------------
1 |
2 | export function convertToHtmlColor(argb: number, hasAlpha?: boolean): string {
3 | var alpha: string;
4 | if (hasAlpha)
5 | alpha = (argb >> 24 & 0xFF).toString(16);
6 | else
7 | alpha = "";
8 | var red: string = (argb >> 16 & 0xFF).toString(16);
9 | var green: string = (argb >> 8 & 0xFF).toString(16);
10 | var blue: string = (argb & 0xFF).toString(16);
11 | if (alpha.length == 1)
12 | alpha = "0" + alpha;
13 | if (red.length == 1)
14 | red = "0" + red;
15 | if (green.length == 1)
16 | green = "0" + green;
17 | if (blue.length == 1)
18 | blue = "0" + blue;
19 | return "#" + alpha + red + green + blue;
20 | }
21 |
22 | export function convertFromHtmlColor(str: string, hasAlpha?: boolean): number {
23 | if (str.length < 1)
24 | return 0;
25 |
26 | if (str.charAt(0) == "#")
27 | str = str.substring(1);
28 |
29 | if (str.length == 8)
30 | return (parseInt(str.substring(0, 2), 16) << 24) + parseInt(str.substring(2), 16);
31 | else if (hasAlpha)
32 | return 0xFF000000 + parseInt(str, 16);
33 | else
34 | return parseInt(str, 16);
35 | }
36 |
37 | export function clamp(value: number, min: number, max: number): number {
38 | if (value < min)
39 | value = min;
40 | else if (value > max)
41 | value = max;
42 | return value;
43 | }
44 |
45 | export function clamp01(value: number): number {
46 | if (isNaN(value))
47 | value = 0;
48 | else if (value > 1)
49 | value = 1;
50 | else if (value < 0)
51 | value = 0;
52 | return value;
53 | }
54 |
55 | export function lerp(start: number, end: number, percent: number): number {
56 | return (start + percent * (end - start));
57 | }
58 |
59 | export function repeat(t: number, length: number): number {
60 | return t - Math.floor(t / length) * length;
61 | }
62 |
63 | export function distance(x1: number, y1: number, x2: number, y2: number): number {
64 | return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
65 | }
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | const gulp = require('gulp')
2 | const rollup = require('rollup')
3 | const ts = require('gulp-typescript');
4 | const rename = require("gulp-rename");
5 | const uglify = require('gulp-uglify-es').default;
6 | const tsProject = ts.createProject('tsconfig.json', { declaration: true });
7 | const dts = require('dts-bundle')
8 |
9 | const onwarn = warning => {
10 | // Silence circular dependency warning for moment package
11 | if (warning.code === 'CIRCULAR_DEPENDENCY')
12 | return
13 |
14 | console.warn(`(!) ${warning.message}`)
15 | }
16 |
17 | gulp.task('buildJs', () => {
18 | return tsProject.src().pipe(tsProject()).pipe(gulp.dest('./build'));
19 | })
20 |
21 | gulp.task("rollup", async function () {
22 | const subTask = await rollup.rollup({
23 | input: "build/FairyGUI.js",
24 | external: ['three'],
25 | onwarn: onwarn,
26 | });
27 | await subTask.write({
28 | file: 'dist/fairygui.js',
29 | format: 'umd',
30 | extend: true,
31 | name: 'fgui',
32 | sourcemap: true,
33 | globals: { three: 'three' }
34 | });
35 |
36 | const subTask2 = await rollup.rollup({
37 | input: "build/FairyGUI.js",
38 | external: ['three'],
39 | });
40 | await subTask2.write({
41 | file: 'dist/fairygui.module.js',
42 | format: 'esm',
43 | extend: true,
44 | name: 'fgui',
45 | sourcemap: true,
46 | globals: { three: 'three' }
47 | });
48 | });
49 |
50 | gulp.task("uglify", function () {
51 | return gulp.src("dist/fairygui.js")
52 | .pipe(rename({ suffix: '.min' }))
53 | .pipe(uglify(/* options */))
54 | .pipe(gulp.dest("dist/"));
55 | });
56 |
57 | gulp.task('buildDts', function () {
58 | return new Promise(function (resolve, reject) {
59 | dts.bundle({ name: "fairygui-three", main: "./build/FairyGUI.d.ts", out: "../dist/fairygui.d.ts" });
60 | resolve();
61 | });
62 | });
63 |
64 | gulp.task('build', gulp.series(
65 | 'buildJs',
66 | 'rollup',
67 | 'uglify',
68 | 'buildDts'
69 | ));
--------------------------------------------------------------------------------
/src/core/mesh/RectMesh.ts:
--------------------------------------------------------------------------------
1 | import { Color4 } from "../../utils/Color";
2 | import { Rect } from "../../utils/Rect";
3 | import { IMeshFactory } from "./MeshFactory";
4 | import { VertexBuffer } from "./VertexBuffer";
5 |
6 | var s_rect: Rect = new Rect();
7 |
8 | export class RectMesh implements IMeshFactory {
9 | public drawRect: Rect;
10 | public lineWidth: number;
11 | public lineColor: Color4;
12 | public fillColor: Color4;
13 |
14 | public constructor() {
15 | this.lineWidth = 1;
16 | }
17 |
18 | public onPopulateMesh(vb: VertexBuffer) {
19 | let rect: Rect = this.drawRect ? this.drawRect : vb.contentRect;
20 | let color: Color4 = this.fillColor ? this.fillColor : vb.vertexColor;
21 | let lineColor: Color4 = this.lineColor ? this.lineColor : vb.vertexColor;
22 |
23 | if (this.lineWidth == 0) {
24 | if (color.a != 0)//optimized
25 | vb.addQuad(rect, null, color);
26 | }
27 | else {
28 | let part: Rect = s_rect;
29 |
30 | //left,right
31 | part.set(rect.x, rect.y, this.lineWidth, rect.height);
32 | vb.addQuad(part, null, lineColor);
33 | part.set(rect.xMax - this.lineWidth, rect.y, this.lineWidth, rect.height);
34 | vb.addQuad(part, null, lineColor);
35 |
36 | //top, bottom
37 | part.set(rect.x + this.lineWidth, rect.y, rect.width - this.lineWidth * 2, this.lineWidth);
38 | vb.addQuad(part, null, lineColor);
39 | part.set(rect.x + this.lineWidth, rect.yMax - this.lineWidth, rect.width - this.lineWidth * 2, this.lineWidth);
40 | vb.addQuad(part, null, lineColor);
41 |
42 | //middle
43 | if (color.a != 0)//optimized
44 | {
45 | part.setMinMax(rect.x + this.lineWidth, rect.y + this.lineWidth, rect.xMax - this.lineWidth, rect.yMax - this.lineWidth);
46 | if (part.width > 0 && part.height > 0)
47 | vb.addQuad(part, null, color);
48 | }
49 | }
50 |
51 | vb.addTriangles();
52 | }
53 | }
--------------------------------------------------------------------------------
/src/tween/GTween.ts:
--------------------------------------------------------------------------------
1 | import { GTweener } from "./GTweener";
2 | import { TweenManager } from "./TweenManager";
3 |
4 | export class GTween {
5 | public static catchCallbackExceptions: boolean = true;
6 |
7 | public static to(start: number, end: number, duration: number): GTweener {
8 | return TweenManager.createTween()._to(start, end, duration);
9 | }
10 |
11 | public static to2(start: number, start2: number, end: number, end2: number, duration: number): GTweener {
12 | return TweenManager.createTween()._to2(start, start2, end, end2, duration);
13 | }
14 |
15 | public static to3(start: number, start2: number, start3: number,
16 | end: number, end2: number, end3: number, duration: number): GTweener {
17 | return TweenManager.createTween()._to3(start, start2, start3, end, end2, end3, duration);
18 | }
19 |
20 | public static to4(start: number, start2: number, start3: number, start4: number,
21 | end: number, end2: number, end3: number, end4: number, duration: number): GTweener {
22 | return TweenManager.createTween()._to4(start, start2, start3, start4, end, end2, end3, end4, duration);
23 | }
24 |
25 | public static toColor(start: number, end: number, duration: number): GTweener {
26 | return TweenManager.createTween()._toColor(start, end, duration);
27 | }
28 |
29 | public static delayedCall(delay: number): GTweener {
30 | return TweenManager.createTween().setDelay(delay);
31 | }
32 |
33 | public static shake(startX: number, startY: number, amplitude: number, duration: number): GTweener {
34 | return TweenManager.createTween()._shake(startX, startY, amplitude, duration);
35 | }
36 |
37 | public static isTweening(target: any, propType?: any): Boolean {
38 | return TweenManager.isTweening(target, propType);
39 | }
40 |
41 | public static kill(target: any, complete?: boolean, propType?: any): void {
42 | TweenManager.killTweens(target, complete, propType);
43 | }
44 |
45 | public static getTween(target: any, propType?: any): GTweener {
46 | return TweenManager.getTween(target, propType);
47 | }
48 | }
49 |
50 |
--------------------------------------------------------------------------------
/src/utils/html/HtmlLink.ts:
--------------------------------------------------------------------------------
1 | import { DisplayObject } from "../../core/DisplayObject";
2 | import { RichTextField } from "../../core/text/RichTextField";
3 | import { SelectionShape } from "../../core/text/SelectionShape";
4 | import { HtmlElement } from "./HtmlElement";
5 | import { IHtmlObject } from "./IHtmlObject";
6 | import { bubbleEvent } from "../../core/Stage";
7 |
8 | export class HtmlLink implements IHtmlObject {
9 | private _owner: RichTextField;
10 | private _element: HtmlElement;
11 | private _shape: SelectionShape;
12 |
13 | public constructor() {
14 | this._shape = new SelectionShape();
15 | this._shape.on("click", () => {
16 | bubbleEvent(this._owner.obj3D, "click_link", this._element.getAttrString("href"));
17 | });
18 | }
19 |
20 | public get displayObject(): DisplayObject {
21 | return this._shape;
22 | }
23 |
24 | public get element(): HtmlElement {
25 | return this._element;
26 | }
27 |
28 | public get width(): number {
29 | return 0;
30 | }
31 |
32 | public get height(): number {
33 | return 0;
34 | }
35 |
36 | public create(owner: RichTextField, element: HtmlElement): void {
37 | this._owner = owner;
38 | this._element = element;
39 | }
40 |
41 | public setArea(startLine: number, startCharX: number, endLine: number, endCharX: number): void {
42 | if (startLine == endLine && startCharX > endCharX) {
43 | let tmp = startCharX;
44 | startCharX = endCharX;
45 | endCharX = tmp;
46 | }
47 | this._shape.rects.length = 0;
48 | this._owner.getLinesShape(startLine, startCharX, endLine, endCharX, true, this._shape.rects);
49 | this._shape.refresh();
50 | }
51 |
52 | public setPosition(x: number, y: number): void {
53 | this._shape.setPosition(x, y);
54 | }
55 |
56 | public add(): void {
57 | this._owner.addChild(this._shape);
58 | }
59 |
60 | public remove(): void {
61 | if (this._shape.parent)
62 | this._owner.removeChild(this._shape);
63 | }
64 |
65 | public release(): void {
66 | this._shape.offAll();
67 | this._owner = null;
68 | this._element = null;
69 | }
70 |
71 | public dispose(): void {
72 | this._shape.dispose();
73 | }
74 | }
--------------------------------------------------------------------------------
/src/ui/PackageItem.ts:
--------------------------------------------------------------------------------
1 | import { Audio } from 'three';
2 | import { PixelHitTestData } from "../core/hittest/PixelHitTest";
3 | import { Frame } from "../core/MovieClip";
4 | import { NTexture } from "../core/NTexture";
5 | import { BitmapFont } from "../core/text/BitmapFont";
6 | import { ByteBuffer } from "../utils/ByteBuffer";
7 | import { Rect } from "../utils/Rect";
8 | import { UIContentScaler } from "./UIContentScaler";
9 | import { UIPackage } from "./UIPackage";
10 |
11 | export class PackageItem {
12 | public owner: UIPackage;
13 |
14 | public type: number;
15 | public objectType: number;
16 |
17 | public id: string;
18 | public name: string;
19 | public width: number = 0;
20 | public height: number = 0;
21 | public file: string;
22 | public decoded?: boolean;
23 | public rawData?: ByteBuffer;
24 |
25 | public highResolution?: Array;
26 | public branches?: Array;
27 |
28 | //image
29 | public scale9Grid?: Rect;
30 | public scaleByTile?: boolean;
31 | public tileGridIndice?: number;
32 | public smoothing?: boolean;
33 | public texture?: NTexture;
34 | public pixelHitTestData?: PixelHitTestData;
35 |
36 | //movieclip
37 | public interval?: number;
38 | public repeatDelay?: number;
39 | public swing?: boolean;
40 | public frames?: Frame[];
41 |
42 | //componenet
43 | public extensionType?: any;
44 |
45 | //font
46 | public bitmapFont?: BitmapFont;
47 |
48 | //sound
49 | public audioBuffer?: AudioBuffer;
50 | public sound?: Audio;
51 |
52 | constructor() {
53 | }
54 |
55 | public load(): Object {
56 | return this.owner.getItemAsset(this);
57 | }
58 |
59 | public getBranch(): PackageItem {
60 | if (this.branches && this.owner._branchIndex != -1) {
61 | var itemId: string = this.branches[this.owner._branchIndex];
62 | if (itemId)
63 | return this.owner.getItemById(itemId);
64 | }
65 |
66 | return this;
67 | }
68 |
69 | public getHighResolution(): PackageItem {
70 | if (this.highResolution && UIContentScaler.scaleLevel > 0) {
71 | var itemId: string = this.highResolution[UIContentScaler.scaleLevel - 1];
72 | if (itemId)
73 | return this.owner.getItemById(itemId);
74 | }
75 |
76 | return this;
77 | }
78 |
79 | public toString(): string {
80 | return this.name;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/tween/GPathPoint.ts:
--------------------------------------------------------------------------------
1 |
2 | export enum CurveType {
3 | CRSpline,
4 | Bezier,
5 | CubicBezier,
6 | Straight
7 | }
8 |
9 | export class GPathPoint {
10 | public x: number;
11 | public y: number;
12 |
13 | public control1_x: number;
14 | public control1_y: number;
15 |
16 | public control2_x: number;
17 | public control2_y: number;
18 |
19 | public curveType: number;
20 |
21 | constructor() {
22 | this.x = 0;
23 | this.y = 0;
24 | this.control1_x = 0;
25 | this.control1_y = 0;
26 | this.control2_x = 0;
27 | this.control2_y = 0;
28 | this.curveType = 0;
29 | }
30 |
31 | public static newPoint(x: number, y: number, curveType: number): GPathPoint {
32 | var pt: GPathPoint = new GPathPoint();
33 | pt.x = x || 0;
34 | pt.y = y || 0;
35 | pt.control1_x = 0;
36 | pt.control1_y = 0;
37 | pt.control2_x = 0;
38 | pt.control2_y = 0;
39 | pt.curveType = curveType || CurveType.CRSpline;
40 |
41 | return pt;
42 | }
43 |
44 | public static newBezierPoint(x: number, y: number, control1_x: number, control1_y: number): GPathPoint {
45 | var pt: GPathPoint = new GPathPoint();
46 | pt.x = x || 0;
47 | pt.y = y || 0;
48 | pt.control1_x = control1_x || 0;
49 | pt.control1_y = control1_y || 0;
50 | pt.control2_x = 0;
51 | pt.control2_y = 0;
52 | pt.curveType = CurveType.Bezier;
53 |
54 | return pt;
55 | }
56 |
57 | public static newCubicBezierPoint(x: number, y: number, control1_x: number, control1_y: number,
58 | control2_x: number, control2_y: number): GPathPoint {
59 | var pt: GPathPoint = new GPathPoint();
60 | pt.x = x || 0;
61 | pt.y = y || 0;
62 | pt.control1_x = control1_x || 0;
63 | pt.control1_y = control1_y || 0;
64 | pt.control2_x = control2_x || 0;
65 | pt.control2_y = control2_y || 0;
66 | pt.curveType = CurveType.CubicBezier;
67 |
68 | return pt;
69 | }
70 |
71 | public clone(): GPathPoint {
72 | var ret: GPathPoint = new GPathPoint();
73 | ret.x = this.x;
74 | ret.y = this.y;
75 | ret.control1_x = this.control1_x;
76 | ret.control1_y = this.control1_y;
77 | ret.control2_x = this.control2_x;
78 | ret.control2_y = this.control2_y;
79 | ret.curveType = this.curveType;
80 |
81 | return ret;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/ui/DragDropManager.ts:
--------------------------------------------------------------------------------
1 | import { Vector2 } from "three";
2 | import { Stage } from "../core/Stage";
3 | import { GLoader } from "./GLoader";
4 | import { GObject } from "./GObject";
5 | import { GRoot } from "./GRoot";
6 | import { Event } from "../event/Event";
7 |
8 | var _inst: DragDropManager;
9 |
10 | export class DragDropManager {
11 |
12 | private _agent: GLoader;
13 | private _sourceData: any;
14 |
15 | public static get inst(): DragDropManager {
16 | if (!_inst)
17 | _inst = new DragDropManager();
18 | return _inst;
19 | }
20 |
21 | constructor() {
22 | let a = this._agent = new GLoader();
23 | a.draggable = true;
24 | a.touchable = false;////important
25 | a.setSize(100, 100);
26 | a.setPivot(0.5, 0.5, true);
27 | a.align = "center";
28 | a.verticalAlign = "middle";
29 | a.sortingOrder = 1000000;
30 | a.on("drag_end", this.__dragEnd, this);
31 | }
32 |
33 | public get dragAgent(): GObject {
34 | return this._agent;
35 | }
36 |
37 | public get dragging(): boolean {
38 | return this._agent.parent != null;
39 | }
40 |
41 | public startDrag(icon: string, sourceData?: any, touchPointID?: number): void {
42 | if (this._agent.parent)
43 | return;
44 |
45 | this._sourceData = sourceData;
46 | this._agent.url = icon;
47 | GRoot.inst.addChild(this._agent);
48 | var pt: Vector2 = GRoot.inst.globalToLocal(Stage.touchPos.x, Stage.touchPos.y);
49 | this._agent.setPosition(pt.x, pt.y);
50 | this._agent.startDrag(touchPointID != null ? touchPointID : -1);
51 | }
52 |
53 | public cancel(): void {
54 | if (this._agent.parent) {
55 | this._agent.stopDrag();
56 | GRoot.inst.removeChild(this._agent);
57 | this._sourceData = null;
58 | }
59 | }
60 |
61 | private __dragEnd(evt: Event): void {
62 | if (this._agent.parent == null) //cancelled
63 | return;
64 |
65 | GRoot.inst.removeChild(this._agent);
66 |
67 | var sourceData: any = this._sourceData;
68 | this._sourceData = null;
69 |
70 | var obj: GObject = GObject.cast(Stage.touchTarget);
71 | while (obj) {
72 | if (obj.hasListener("drop")) {
73 | obj.dispatchEvent("drop", sourceData);
74 | return;
75 | }
76 |
77 | obj = obj.parent;
78 | }
79 | }
80 | }
81 |
82 |
--------------------------------------------------------------------------------
/test/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const HtmlWebpackPlugin = require('html-webpack-plugin');
3 | const { CleanWebpackPlugin } = require("clean-webpack-plugin");
4 | const CopyWebpackPlugin = require('copy-webpack-plugin');
5 | const TerserPlugin = require('terser-webpack-plugin');
6 |
7 | module.exports = {
8 | mode: 'development',
9 | entry: path.resolve(__dirname, './src/index.ts'),
10 | devtool: 'source-map',
11 | plugins: [
12 | new CleanWebpackPlugin(),
13 | new HtmlWebpackPlugin({
14 | title: 'FairyGUI-threejs test',
15 | template: path.resolve(__dirname, 'template.html')
16 | }),
17 | new CopyWebpackPlugin([
18 | {
19 | from:path.resolve(__dirname, 'assets'),
20 | to:path.resolve(__dirname, 'build/assets')
21 | }
22 | ])
23 | ],
24 | resolve: {
25 | extensions: ['.tsx', '.ts', '.js']
26 | },
27 | output: {
28 | filename: 'bundle.js',
29 | path: path.resolve(__dirname, 'build')
30 | },
31 | module: {
32 | rules: [
33 | {
34 | test: /\.tsx?$/,
35 | use: 'ts-loader',
36 | exclude: /node_modules/
37 | },
38 | {
39 | test: /\.css$/,
40 | use: [
41 | 'style-loader',
42 | 'css-loader'
43 | ]
44 | },
45 | {
46 | test: /\.(png|svg|jpg|gif)$/,
47 | use: [
48 | 'file-loader'
49 | ]
50 | },
51 | {
52 | test: /\.(woff|woff2|eot|ttf|otf)$/,
53 | use: [
54 | 'file-loader'
55 | ]
56 | },
57 | {
58 | test: /\.(csv|tsv)$/,
59 | use: [
60 | 'csv-loader'
61 | ]
62 | },
63 | {
64 | test: /\.xml$/,
65 | use: [
66 | 'xml-loader'
67 | ]
68 | }
69 | ]
70 | },
71 |
72 | optimization: {
73 | minimize: true,
74 | minimizer: [new TerserPlugin({ sourceMap: true })],
75 | splitChunks: {
76 | chunks: 'all',
77 | automaticNameDelimiter: '_',
78 | name: true,
79 | cacheGroups: {
80 | three: {
81 | name: 'three',
82 | chunks: 'all',
83 | test: /[\\/]node_modules[\\/](three)[\\/]/,
84 | }
85 | },
86 | }
87 | }
88 | };
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FairyGUI-three
2 |
3 | #### A GUI Editor&framework for Three.js ####
4 |
5 | Official website: [www.fairygui.com](https://www.fairygui.com)
6 |
7 | ### Usage ###
8 |
9 | Step 1, we use the editor to create the UI.
10 |
11 | 
12 |
13 | Step 2, we only need a little code to display it.
14 |
15 | ```javascript
16 | import * as fgui from "fairygui-three";
17 |
18 | var renderer;
19 | var scene;
20 | var view;
21 |
22 | init();
23 | animate();
24 |
25 | function init() {
26 | //THREE initialization code here
27 |
28 | fgui.Stage.init(renderer, { screenMode:'horizontal' }); //screenMode is optional if you dont want to rotate the screen
29 | fgui.Stage.scene = scene;
30 |
31 | fgui.UIPackage.loadPackage('path/to/UI').then(()=> {
32 | view = fgui.UIPackage.CreateObject('Basics', 'Main');
33 | view.makeFullScreen();
34 | fgui.GRoot.inst.addChild(view);
35 | });
36 | }
37 |
38 | function animate() {
39 |
40 | requestAnimationFrame( animate );
41 |
42 | fgui.Stage.update();
43 | renderer.render(scene, fgui.Stage.camera);
44 | }
45 | ```
46 |
47 | You should see [this](https://fairygui.com/threejs-demo/main/)
48 |
49 | In the example above, an UI is created and displayed by an orthographic camera (fgui.Stage.camera) . It's easy to display UI by an specific perspective camera.
50 |
51 | ```javascript
52 | import * as fgui from "fairygui-three";
53 |
54 | var renderer;
55 | var scene;
56 | var camera;
57 | var view;
58 |
59 | init();
60 | animate();
61 |
62 | function init() {
63 | //THREE initialization code here
64 |
65 | camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 10 );
66 | camera.position.z = 1;
67 |
68 | fgui.Stage.init(renderer);
69 | fgui.Stage.scene = scene;
70 |
71 | fgui.UIPackage.loadPackage('path/to/UI').then(()=> {
72 | view = fgui.UIPackage.CreateObject('3DInventory', 'Main');
73 | view.displayObject.camera = camera;
74 | view.displayObject.setLayer(0);
75 |
76 | let container = new Group();
77 | container.scale.set(0.5, 0.5, 0.5);
78 | container.add(view.obj3D);
79 | scene.add(container);
80 | });
81 | }
82 |
83 | function animate() {
84 |
85 | requestAnimationFrame( animate );
86 |
87 | fgui.Stage.update();
88 | renderer.render(scene, camera);
89 | }
90 | ```
91 |
92 | You should see [this](https://fairygui.com/threejs-demo/3d/)
93 |
94 | If a perspective camera is using for all UI,
95 |
96 | ```javascript
97 | Stage.init(renderer, { defaultLayer:0 });
98 | Stage.camera = yourCamera;
99 | ```
100 |
101 | # License
102 | MIT
103 |
--------------------------------------------------------------------------------
/src/ui/GTextInput.ts:
--------------------------------------------------------------------------------
1 | import { GTextField } from "./GTextField";
2 | import { ByteBuffer } from "../utils/ByteBuffer";
3 | import { InputTextField } from "../core/text/InputTextField";
4 |
5 | export class GTextInput extends GTextField {
6 | constructor() {
7 | super();
8 | }
9 |
10 | protected createDisplayObject(): void {
11 | this._displayObject = this._textField = new InputTextField();
12 | }
13 |
14 | public get password(): boolean {
15 | return (this._textField).password;
16 | }
17 |
18 | public set password(value: boolean) {
19 | (this._textField).password = value;
20 | }
21 |
22 | public get keyboardType(): string {
23 | return (this._textField).keyboardType;
24 | }
25 |
26 | public set keyboardType(value: string) {
27 | (this._textField).keyboardType = value;
28 | }
29 |
30 | public set editable(value: boolean) {
31 | (this._textField).editable = value;
32 | }
33 |
34 | public get editable(): boolean {
35 | return (this._textField).editable;
36 | }
37 |
38 | public set maxLength(value: number) {
39 | (this._textField).maxLength = value;
40 | }
41 |
42 | public get maxLength(): number {
43 | return (this._textField).maxLength;
44 | }
45 |
46 | public set promptText(value: string) {
47 | (this._textField).promptText = value;
48 | }
49 |
50 | public get promptText(): string {
51 | return (this._textField).promptText;
52 | }
53 |
54 | public set restrict(value: string) {
55 | (this._textField).restrict = value;
56 | }
57 |
58 | public get restrict(): string {
59 | return (this._textField).restrict;
60 | }
61 |
62 | public requestFocus(): void {
63 | }
64 |
65 | public setup_beforeAdd(buffer: ByteBuffer, beginPos: number): void {
66 | super.setup_beforeAdd(buffer, beginPos);
67 |
68 | buffer.seek(beginPos, 4);
69 |
70 | var str: string = buffer.readS();
71 | if (str != null)
72 | this.promptText = str;
73 |
74 | str = buffer.readS();
75 | if (str != null)
76 | this.restrict = str;
77 |
78 | var iv: number = buffer.readInt();
79 | if (iv != 0)
80 | this.maxLength = iv;
81 | iv = buffer.readInt();
82 | if (iv != 0) {
83 | if (iv == 4)
84 | this.keyboardType = "number";
85 | else if (iv == 3)
86 | this.keyboardType = "url";
87 | }
88 | if (buffer.readBool())
89 | this.password = true;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/utils/html/HtmlImage.ts:
--------------------------------------------------------------------------------
1 | import { DisplayObject } from "../../core/DisplayObject";
2 | import { RichTextField } from "../../core/text/RichTextField";
3 | import { LoaderFillType, ObjectType } from "../../ui/FieldTypes";
4 | import { GLoader } from "../../ui/GLoader";
5 | import { PackageItem } from "../../ui/PackageItem";
6 | import { UIPackage, Decls } from "../../ui/UIPackage";
7 | import { HtmlElement } from "./HtmlElement";
8 | import { IHtmlObject } from "./IHtmlObject";
9 |
10 | export class HtmlImage implements IHtmlObject {
11 | public readonly loader: GLoader;
12 |
13 | private _owner: RichTextField;
14 | private _element: HtmlElement;
15 |
16 | public constructor() {
17 | this.loader = Decls.UIObjectFactory.newObject(ObjectType.Loader);
18 | this.loader.fill = LoaderFillType.ScaleFree;
19 | this.loader.touchable = false;
20 | }
21 |
22 | public get displayObject(): DisplayObject {
23 | return this.loader.displayObject;
24 | }
25 |
26 | public get element(): HtmlElement {
27 | return this._element;
28 | }
29 |
30 | public get width(): number {
31 | return this.loader.width;
32 | }
33 |
34 | public get height(): number {
35 | return this.loader.height;
36 | }
37 |
38 | public create(owner: RichTextField, element: HtmlElement): void {
39 | this._owner = owner;
40 | this._element = element;
41 |
42 | let sourceWidth = 0;
43 | let sourceHeight = 0;
44 | let src: string = element.getAttrString("src");
45 | if (src != null) {
46 | let pi: PackageItem = UIPackage.getItemByURL(src);
47 | if (pi) {
48 | sourceWidth = pi.width;
49 | sourceHeight = pi.height;
50 | }
51 | }
52 |
53 | this.loader.url = src;
54 |
55 | let width = element.getAttrInt("width", sourceWidth);
56 | let height = element.getAttrInt("height", sourceHeight);
57 |
58 | if (width == 0)
59 | width = 5;
60 | if (height == 0)
61 | height = 10;
62 | this.loader.setSize(width, height);
63 | }
64 |
65 | public setPosition(x: number, y: number): void {
66 | this.loader.setPosition(x, y);
67 | }
68 |
69 | public add(): void {
70 | this._owner.addChild(this.loader.displayObject);
71 | }
72 |
73 | public remove(): void {
74 | if (this.loader.displayObject.parent)
75 | this._owner.removeChild(this.loader.displayObject);
76 | }
77 |
78 | public release(): void {
79 | this.loader.offAll();
80 | this.loader.url = null;
81 | this._owner = null;
82 | this._element = null;
83 | }
84 |
85 | public dispose(): void {
86 | this.loader.dispose();
87 | }
88 | }
--------------------------------------------------------------------------------
/src/FairyGUI.ts:
--------------------------------------------------------------------------------
1 | export { GGroup } from "./ui/GGroup";
2 | export { GObject } from "./ui/GObject";
3 | export { GGraph } from "./ui/GGraph";
4 | export { GImage } from "./ui/GImage";
5 | export { GMovieClip } from "./ui/GMovieClip";
6 | export { GRoot } from "./ui/GRoot";
7 | export { GTextField } from "./ui/GTextField";
8 | export { GRichTextField } from "./ui/GRichTextField";
9 | export { GTextInput } from "./ui/GTextInput";
10 | export { GLoader } from "./ui/GLoader";
11 | export { GLoader3D } from "./ui/GLoader3D";
12 | export { GComponent } from "./ui/GComponent";
13 | export { GLabel } from "./ui/GLabel";
14 | export { GButton } from "./ui/GButton";
15 | export { GComboBox } from "./ui/GComboBox";
16 | export { GSlider } from "./ui/GSlider";
17 | export { GProgressBar } from "./ui/GProgressBar";
18 | export { GScrollBar } from "./ui/GScrollBar";
19 | export { GList } from "./ui/GList";
20 | export { GTree } from "./ui/GTree";
21 | export { GTreeNode } from "./ui/GTreeNode";
22 | export { Window } from "./ui/Window";
23 | export { PopupMenu } from "./ui/PopupMenu";
24 | export { Controller } from "./ui/Controller";
25 | export { Transition } from "./ui/Transition";
26 | export { ScrollPane } from "./ui/ScrollPane";
27 | export { RelationType } from "./ui/FieldTypes";
28 | export { UIPackage } from "./ui/UIPackage";
29 | export { PackageItem } from "./ui/PackageItem";
30 | export { GObjectPool } from "./ui/GObjectPool";
31 | export { UIObjectFactory } from "./ui/UIObjectFactory";
32 | export { UIConfig } from "./ui/UIConfig";
33 | export { DragDropManager } from "./ui/DragDropManager";
34 | export { AsyncOperation } from "./ui/AsyncOperation";
35 | export { TranslationHelper } from "./ui/TranslationHelper";
36 | export * from "./ui/UIContentScaler";
37 | export * from "./ui/FieldTypes"
38 |
39 | export { DisplayObject } from "./core/DisplayObject";
40 | export { Image } from "./core/Image";
41 | export { MovieClip, Frame } from "./core/MovieClip";
42 | export { Shape } from "./core/Shape";
43 | export { Stage } from "./core/Stage";
44 | export { NTexture } from "./core/NTexture";
45 | export { NGraphics } from "./core/NGraphics";
46 | export { NMaterial } from "./core/NMaterial";
47 | export { TextField } from "./core/text/TextField"
48 | export { RichTextField } from "./core/text/RichTextField"
49 | export { InputTextField } from "./core/text/InputTextField"
50 | export { TextFormat } from "./core/text/TextFormat"
51 | export { DynamicFont } from "./core/text/DynamicFont"
52 | export { FontManager } from "./core/text/FontManager"
53 |
54 | export { Event } from "./event/Event";
55 | export { EventDispatcher } from "./event/EventDispatcher";
56 |
57 | export { GTween } from "./tween/GTween";
58 | export { GTweener } from "./tween/GTweener";
59 | export { EaseType } from "./tween/EaseType";
60 |
61 | export { UBBParser } from "./utils/UBBParser";
62 | export { ByteBuffer } from "./utils/ByteBuffer";
63 | export { Rect } from "./utils/Rect";
64 | export { Color4 } from "./utils/Color";
65 | export { Timers } from "./utils/Timers";
66 | export * from "./utils/ToolSet"
--------------------------------------------------------------------------------
/src/event/Event.ts:
--------------------------------------------------------------------------------
1 | import { Pool } from "../utils/Pool";
2 | import { DisplayObject } from "../core/DisplayObject";
3 | import { EventDispatcher } from "./EventDispatcher";
4 |
5 | export type EventType = "touch_begin" | "touch_end" | "touch_move" | "click" | "right_click" | "roll_over" | "roll_out" | "mouse_wheel"
6 | | "content_scale_factor_changed"
7 | | "added_to_stage" | "removed_from_stage"
8 | | "pos_changed" | "size_changed"
9 | | "status_changed"
10 | | "focus_in" | "focus_out"
11 | | "drag_start" | "drag_move" | "drag_end" | "drop"
12 | | "scroll" | "scroll_end" | "pull_down_release" | "pull_up_release"
13 | | "click_item" | "click_link"
14 | | "play_end" | "gear_stop";
15 |
16 | export interface InputInfo {
17 | x: number;
18 | y: number;
19 | mouseWheelDelta: number;
20 | touchId: number;
21 | button: number;
22 | clickCount: number;
23 | holdTime: number;
24 | shiftKey?: boolean;
25 | ctrlKey?: boolean;
26 | commandKey?: boolean;
27 |
28 | isDblClick: boolean;
29 | isRightButton: boolean;
30 | }
31 |
32 | export var lastInput: InputInfo = {
33 | x: 0,
34 | y: 0,
35 | mouseWheelDelta: 0,
36 | touchId: 0,
37 | button: 0,
38 | clickCount: 0,
39 | holdTime: 0,
40 |
41 | get isDblClick() {
42 | return this.clickCount == 2;
43 | },
44 |
45 | get isRightButton() {
46 | return this.button == 2;
47 | }
48 | }
49 |
50 | export class Event {
51 | public data: any = null;
52 |
53 | public _defaultPrevented: boolean;
54 | public _stopsPropagation: boolean;
55 | public _touchCapture: boolean;
56 | public _callChain: Array = [];
57 |
58 | public _type: string;
59 | public _sender: EventDispatcher;
60 | public _initiator: DisplayObject;
61 |
62 | public constructor() {
63 | }
64 |
65 | public get type(): string {
66 | return this._type;
67 | }
68 |
69 | public get sender(): EventDispatcher {
70 | return this._sender;
71 | }
72 |
73 | public get initiator(): DisplayObject {
74 | return this._initiator;
75 | }
76 |
77 | public get input(): InputInfo {
78 | return lastInput;
79 | }
80 |
81 | public stopPropagation() {
82 | this._stopsPropagation = true;
83 | }
84 |
85 | public preventDefault() {
86 | this._defaultPrevented = true;
87 | }
88 |
89 | public captureTouch() {
90 | this._touchCapture = true;
91 | }
92 |
93 | public get isDefaultPrevented(): boolean {
94 | return this._defaultPrevented;
95 | }
96 | }
97 |
98 | export var EventPool: Pool = new Pool(Event,
99 | obj => {
100 | obj._stopsPropagation = false;
101 | obj._defaultPrevented = false;
102 | obj._touchCapture = false;
103 | },
104 | obj => {
105 | obj.data = null;
106 | obj._initiator = null;
107 | obj._sender = null;
108 | });
--------------------------------------------------------------------------------
/src/ui/UIConfig.ts:
--------------------------------------------------------------------------------
1 | import { Color4 } from "../utils/Color";
2 | import { ScrollBarDisplayType } from "./FieldTypes";
3 |
4 | export class UIConfig {
5 | //Default font name
6 | public static defaultFont: string = "Arial";
7 |
8 | //Resource using in Window.ShowModalWait for locking the window.
9 | public static windowModalWaiting: string;
10 | //Resource using in GRoot.ShowModalWait for locking the screen.
11 | public static globalModalWaiting: string;
12 |
13 | //When a modal window is in front, the background becomes dark.
14 | public static modalLayerColor: Color4 = new Color4(0x333333, 0.2);
15 |
16 | //Default button click sound
17 | public static buttonSound: string = null;
18 | public static buttonSoundVolumeScale: number = 1;
19 |
20 | //Default button click sound
21 | public static horizontalScrollBar: string = null;
22 |
23 | public static verticalScrollBar: string = null;
24 |
25 | //Scrolling step in pixels
26 | public static defaultScrollStep: number = 25;
27 | //Deceleration ratio of scrollpane when its in touch dragging.
28 | public static defaultScrollDecelerationRate: number = 0.967;
29 |
30 | //Default scrollbar display mode. Recommened visible for Desktop and Auto for mobile.
31 | public static defaultScrollBarDisplay: number = ScrollBarDisplayType.Visible;
32 | //Allow dragging the content to scroll. Recommeded true for mobile.
33 | public static defaultScrollTouchEffect: boolean = true;
34 | //The "rebound" effect in the scolling container. Recommeded true for mobile.
35 | public static defaultScrollBounceEffect: boolean = true;
36 |
37 | /**
38 | * 当滚动容器设置为“贴近ITEM”时,判定贴近到哪一个ITEM的滚动距离阀值。
39 | */
40 | public static defaultScrollSnappingThreshold: number = 0.1;
41 |
42 | /**
43 | * 当滚动容器设置为“页面模式”时,判定翻到哪一页的滚动距离阀值。
44 | */
45 | public static defaultScrollPagingThreshold: number = 0.3;
46 |
47 | //Resources for PopupMenu.
48 | public static popupMenu: string = null;
49 | //Resources for seperator of PopupMenu.
50 | public static popupMenu_seperator: string = null;
51 | //In case of failure of loading content for GLoader, use this sign to indicate an error.
52 | public static loaderErrorSign: string = null;
53 | //Resources for tooltips.
54 | public static tooltipsWin: string = null;
55 |
56 | //Max items displayed in combobox without scrolling.
57 | public static defaultComboBoxVisibleItemCount: number = 10;
58 |
59 | // Pixel offsets of finger to trigger scrolling.
60 | public static touchScrollSensitivity: number = 20;
61 |
62 | // Pixel offsets of finger to trigger dragging.
63 | public static touchDragSensitivity: number = 10;
64 |
65 | // Pixel offsets of mouse pointer to trigger dragging.
66 | public static clickDragSensitivity: number = 2;
67 |
68 | // When click the window, brings to front automatically.
69 | public static bringWindowToFrontOnClick: boolean = true;
70 |
71 | public static frameTimeForAsyncUIConstruction: number = 2;
72 |
73 | public static packageFileExtension: string = "fui";
74 | }
75 |
--------------------------------------------------------------------------------
/src/utils/html/HtmlElement.ts:
--------------------------------------------------------------------------------
1 | import { Vector2 } from "three";
2 | import { TextFormat } from "../../core/text/TextFormat";
3 | import { Pool } from "../Pool";
4 | import { XMLIterator } from "../xml/XMLIterator";
5 | import { XMLUtils } from "../xml/XMLUtils";
6 | import { IHtmlObject } from "./IHtmlObject";
7 |
8 | export enum HtmlElementType {
9 | Text,
10 | Link,
11 | Image,
12 | Input,
13 | Select,
14 | Object,
15 |
16 | //internal
17 | LinkEnd,
18 | }
19 |
20 | export class HtmlElement {
21 | public type: HtmlElementType;
22 | public name: string;
23 | public text: string;
24 | public format: TextFormat;
25 | public charIndex: number;
26 |
27 | public htmlObject: IHtmlObject;
28 | public status: number; //1 hidden 2 clipped 4 added
29 | public space: number;
30 | public position: Vector2;
31 |
32 | public _attributes: Record;
33 |
34 | public constructor() {
35 | this.format = new TextFormat();
36 | this.position = new Vector2();
37 | }
38 |
39 | public getAttr(attrName: string): any {
40 | if (this._attributes == null)
41 | return null;
42 |
43 | return this._attributes[attrName];
44 | }
45 |
46 | public setAttr(attrName: string, attrValue: any) {
47 | if (this._attributes == null)
48 | this._attributes = {};
49 |
50 | this._attributes[attrName] = attrValue;
51 | }
52 |
53 | public getAttrString(attrName: string, defValue?: string) {
54 | return XMLUtils.getString(this._attributes, attrName, defValue);
55 | }
56 |
57 | public getAttrInt(attrName: string, defValue?: number): number {
58 | return XMLUtils.getInt(this._attributes, attrName, defValue);
59 | }
60 |
61 | public getAttrFloat(attrName: string, defValue?: number): number {
62 | return XMLUtils.getFloat(this._attributes, attrName, defValue);
63 | }
64 |
65 | public getAttrBool(attrName: string, defValue?: boolean): boolean {
66 | return XMLUtils.getBool(this._attributes, attrName, defValue);
67 | }
68 |
69 | public getAttrColor(attrName: string, defValue?: number): number {
70 | return XMLUtils.getColor(this._attributes, attrName, defValue);
71 | }
72 |
73 | public fetchAttributes() {
74 | this._attributes = XMLIterator.getAttributes(this._attributes);
75 | }
76 |
77 | public get isEntity(): boolean {
78 | return this.type == HtmlElementType.Image || this.type == HtmlElementType.Select
79 | || this.type == HtmlElementType.Input || this.type == HtmlElementType.Object;
80 | }
81 |
82 | public reset() {
83 | this.name = null;
84 | this.text = null;
85 | this.htmlObject = null;
86 | this.status = 0;
87 | this._attributes = null;
88 | }
89 | }
90 |
91 | export var elementPool = new Pool(HtmlElement,
92 | (element, ...argArray: any[]) => {
93 | element.type = argArray[0];
94 | if (element.type != HtmlElementType.Text && element._attributes == null)
95 | element._attributes = {};
96 | },
97 | element => element.reset());
--------------------------------------------------------------------------------
/src/core/mesh/RegularPolygonMesh.ts:
--------------------------------------------------------------------------------
1 | import { IMeshFactory } from "./MeshFactory";
2 | import { IHitTest } from "../hittest/IHitTest";
3 | import { Rect } from "../../utils/Rect";
4 | import { VertexBuffer } from "./VertexBuffer";
5 | import { Color4 } from "../../utils/Color";
6 |
7 | export class RegularPolygonMesh implements IMeshFactory, IHitTest {
8 | public drawRect: Rect;
9 | public sides: number;
10 | public lineWidth: number;
11 | public lineColor: Color4;
12 | public centerColor: Color4;
13 | public fillColor: Color4;
14 | public distances: Array;
15 | public rotation: number;
16 |
17 | public constructor() {
18 | this.sides = 3;
19 | this.lineWidth = 1;
20 | this.lineColor = new Color4();
21 | }
22 |
23 | public onPopulateMesh(vb: VertexBuffer) {
24 | if (this.distances != null && this.distances.length < this.sides) {
25 | console.error("distances.Length 0) {
47 | vb.addVert(xv + centerX, yv + centerY, 0, this.lineColor);
48 |
49 | xv = Math.cos(angle) * r + centerX;
50 | yv = Math.sin(angle) * r + centerY;
51 | vb.addVert(xv, yv, 0, this.lineColor);
52 | }
53 | angle += angleDelta;
54 | }
55 |
56 | if (this.lineWidth > 0) {
57 | let tmp: number = this.sides * 3;
58 | for (let i: number = 0; i < tmp; i += 3) {
59 | if (i != tmp - 3) {
60 | vb.addTriangle(0, i + 4, i + 1);
61 | vb.addTriangle(i + 5, i + 3, i + 2);
62 | vb.addTriangle(i + 3, i + 5, i + 6);
63 | }
64 | else {
65 | vb.addTriangle(0, 1, i + 1);
66 | vb.addTriangle(2, i + 3, i + 2);
67 | vb.addTriangle(i + 3, 2, 3);
68 | }
69 | }
70 | }
71 | else {
72 | for (let i: number = 0; i < this.sides; i++)
73 | vb.addTriangle(0, (i == this.sides - 1) ? 1 : i + 2, i + 1);
74 | }
75 | }
76 |
77 | public hitTest(contentRect: Rect, x: number, y: number): boolean {
78 | if (this.drawRect)
79 | return this.drawRect.contains(x, y);
80 | else
81 | return contentRect.contains(x, y);
82 | }
83 | }
--------------------------------------------------------------------------------
/src/ui/UIContentScaler.ts:
--------------------------------------------------------------------------------
1 | import { Stage, broadcastEvent } from "../core/Stage";
2 |
3 | export enum ScaleMode {
4 | ConstantPixelSize,
5 | ScaleWithScreenSize,
6 | ConstantPhysicalSize
7 | }
8 |
9 | export enum ScreenMatchMode {
10 | MatchWidthOrHeight,
11 | MatchWidth,
12 | MatchHeight
13 | }
14 |
15 | export class UIContentScaler {
16 | public static get scaleFactor() { return _scaleFactor; }
17 | public static get scaleLevel() { return _scaleLevel; }
18 |
19 | public static scaleWithScreenSize(designResolutionX: number, designResolutionY: number, screenMatchMode?: ScreenMatchMode) {
20 | _designResolutionX = designResolutionX;
21 | _designResolutionY = designResolutionY;
22 | _scaleMode = ScaleMode.ScaleWithScreenSize;
23 | _screenMatchMode = screenMatchMode || ScreenMatchMode.MatchWidthOrHeight;
24 | refresh();
25 | }
26 |
27 | public static setConstant(constantScaleFactor?: number) {
28 | _scaleMode = ScaleMode.ConstantPixelSize;
29 | _constantScaleFactor = constantScaleFactor || 1;
30 | refresh();
31 | }
32 | }
33 |
34 | var _scaleMode: ScaleMode = ScaleMode.ConstantPixelSize;
35 | var _screenMatchMode: ScreenMatchMode;
36 | var _designResolutionX: number = 1136;
37 | var _designResolutionY: number = 640;
38 | // var _fallbackScreenDPI: number;
39 | // var _defaultSpriteDPI: number;
40 | var _constantScaleFactor: number = 1;
41 | var _ignoreOrientation: boolean;
42 |
43 | var _scaleFactor: number = 1;
44 | var _scaleLevel: number = 0;
45 |
46 | Stage.eventDispatcher.on("size_changed", refresh);
47 |
48 | function refresh() {
49 | let screenWidth: number = Stage.width;
50 | let screenHeight: number = Stage.height;
51 |
52 | if (_scaleMode == ScaleMode.ScaleWithScreenSize) {
53 | if (_designResolutionX == 0 || _designResolutionY == 0)
54 | return;
55 |
56 | let dx = _designResolutionX;
57 | let dy = _designResolutionY;
58 | if (!_ignoreOrientation && (screenWidth > screenHeight && dx < dy || screenWidth < screenHeight && dx > dy)) {
59 | //scale should not change when orientation change
60 | let tmp = dx;
61 | dx = dy;
62 | dy = tmp;
63 | }
64 |
65 | if (_screenMatchMode == ScreenMatchMode.MatchWidthOrHeight) {
66 | let s1 = screenWidth / dx;
67 | let s2 = screenHeight / dy;
68 | _scaleFactor = Math.min(s1, s2);
69 | }
70 | else if (_screenMatchMode == ScreenMatchMode.MatchWidth)
71 | _scaleFactor = screenWidth / dx;
72 | else
73 | _scaleFactor = screenHeight / dy;
74 | }
75 | else if (_scaleMode == ScaleMode.ConstantPhysicalSize) {
76 | // let dpi = Screen.dpi;
77 | // if (dpi == 0)
78 | // dpi = _fallbackScreenDPI;
79 | // if (dpi == 0)
80 | // dpi = 96;
81 | // _scaleFactor = dpi / (defaultSpriteDPI == 0 ? 96 : defaultSpriteDPI);
82 | }
83 | else
84 | _scaleFactor = _constantScaleFactor;
85 |
86 | if (_scaleFactor > 10)
87 | _scaleFactor = 10;
88 |
89 | if (_scaleFactor > 3)
90 | _scaleLevel = 3; //x4
91 | else if (_scaleFactor > 2)
92 | _scaleLevel = 2; //x3
93 | else if (_scaleFactor > 1)
94 | _scaleLevel = 1; //x2
95 | else
96 | _scaleLevel = 0;
97 |
98 | broadcastEvent(Stage.scene, "content_scale_factor_changed");
99 | }
--------------------------------------------------------------------------------
/src/ui/FieldTypes.ts:
--------------------------------------------------------------------------------
1 |
2 | export type AlignType = "left" | "center" | "right";
3 | export type VertAlignType = "top" | "middle" | "bottom";
4 |
5 | export enum ButtonMode {
6 | Common,
7 | Check,
8 | Radio
9 | }
10 | export enum AutoSizeType {
11 | None,
12 | Both,
13 | Height,
14 | Shrink
15 | }
16 |
17 | export enum LoaderFillType {
18 | None,
19 | Scale,
20 | ScaleMatchHeight,
21 | ScaleMatchWidth,
22 | ScaleFree,
23 | ScaleNoBorder
24 | }
25 | export enum ListLayoutType {
26 | SingleColumn,
27 | SingleRow,
28 | FlowHorizontal,
29 | FlowVertical,
30 | Pagination
31 | }
32 | export enum ListSelectionMode {
33 | Single,
34 | Multiple,
35 | Multiple_SingleClick,
36 | None
37 | }
38 | export enum OverflowType {
39 | Visible,
40 | Hidden,
41 | Scroll
42 | }
43 | export enum PackageItemType {
44 | Image,
45 | MovieClip,
46 | Sound,
47 | Component,
48 | Atlas,
49 | Font,
50 | Swf,
51 | Misc,
52 | Unknown
53 | }
54 | export enum ObjectType {
55 | Image,
56 | MovieClip,
57 | Swf,
58 | Graph,
59 | Loader,
60 | Group,
61 | Text,
62 | RichText,
63 | InputText,
64 | Component,
65 | List,
66 | Label,
67 | Button,
68 | ComboBox,
69 | ProgressBar,
70 | Slider,
71 | ScrollBar,
72 | Tree,
73 | Loader3D
74 | }
75 | export enum ProgressTitleType {
76 | Percent,
77 | ValueAndMax,
78 | Value,
79 | Max
80 | }
81 | export enum ScrollBarDisplayType {
82 | Default,
83 | Visible,
84 | Auto,
85 | Hidden
86 | }
87 | export enum ScrollType {
88 | Horizontal,
89 | Vertical,
90 | Both
91 | }
92 | export enum FlipType {
93 | None,
94 | Horizontal,
95 | Vertical,
96 | Both
97 | }
98 | export enum ChildrenRenderOrder {
99 | Ascent,
100 | Descent,
101 | Arch
102 | }
103 | export enum GroupLayoutType {
104 | None,
105 | Horizontal,
106 | Vertical
107 | }
108 | export enum PopupDirection {
109 | Auto,
110 | Up,
111 | Down
112 | }
113 | export enum RelationType {
114 | Left_Left = 0,
115 | Left_Center = 1,
116 | Left_Right = 2,
117 | Center_Center = 3,
118 | Right_Left = 4,
119 | Right_Center = 5,
120 | Right_Right = 6,
121 |
122 | Top_Top = 7,
123 | Top_Middle = 8,
124 | Top_Bottom = 9,
125 | Middle_Middle = 10,
126 | Bottom_Top = 11,
127 | Bottom_Middle = 12,
128 | Bottom_Bottom = 13,
129 |
130 | Width = 14,
131 | Height = 15,
132 |
133 | LeftExt_Left = 16,
134 | LeftExt_Right = 17,
135 | RightExt_Left = 18,
136 | RightExt_Right = 19,
137 | TopExt_Top = 20,
138 | TopExt_Bottom = 21,
139 | BottomExt_Top = 22,
140 | BottomExt_Bottom = 23,
141 |
142 | Size = 24
143 | }
144 |
145 | export enum FillMethod {
146 | None,
147 | Horizontal,
148 | Vertical,
149 | Radial90,
150 | Radial180,
151 | Radial360,
152 | }
153 |
154 | export enum FillOrigin {
155 | Top,
156 | Bottom,
157 | Left,
158 | Right,
159 |
160 | TopLeft = 0,
161 | TopRight = 1,
162 | BottomLeft = 2,
163 | BottomRight = 3
164 | }
165 |
166 | export enum FillOrigin90 {
167 | TopLeft,
168 | TopRight,
169 | BottomLeft,
170 | BottomRight
171 | }
172 |
173 | export enum ObjectPropID {
174 | Text,
175 | Icon,
176 | Color,
177 | OutlineColor,
178 | Playing,
179 | Frame,
180 | DeltaTime,
181 | TimeScale,
182 | FontSize,
183 | Selected
184 | }
185 |
--------------------------------------------------------------------------------
/src/ui/GGraph.ts:
--------------------------------------------------------------------------------
1 | import { GObject } from "./GObject";
2 | import { ObjectPropID } from "./FieldTypes";
3 | import { ByteBuffer } from "../utils/ByteBuffer";
4 | import { Shape } from "../core/Shape";
5 |
6 | export class GGraph extends GObject {
7 | private _shape: Shape;
8 |
9 | constructor() {
10 | super();
11 | }
12 |
13 | public get shape(): Shape {
14 | return this._shape;
15 | }
16 |
17 | public get color(): number {
18 | return this._shape.graphics.color;
19 | }
20 |
21 | public set color(value: number) {
22 | if (this._shape.graphics.color != value) {
23 | this._shape.graphics.color = value;
24 | this.updateGear(4);
25 | }
26 | }
27 |
28 | protected createDisplayObject(): void {
29 | this._displayObject = this._shape = new Shape();
30 | }
31 |
32 | public getProp(index: number): any {
33 | if (index == ObjectPropID.Color)
34 | return this.color;
35 | else
36 | return super.getProp(index);
37 | }
38 |
39 | public setProp(index: number, value: any): void {
40 | if (index == ObjectPropID.Color)
41 | this.color = value;
42 | else
43 | super.setProp(index, value);
44 | }
45 |
46 | public setup_beforeAdd(buffer: ByteBuffer, beginPos: number): void {
47 | super.setup_beforeAdd(buffer, beginPos);
48 |
49 | buffer.seek(beginPos, 5);
50 |
51 | let type = buffer.readByte();
52 | if (type != 0) {
53 | let i: number;
54 | let cnt: number;
55 |
56 | let lineSize = buffer.readInt();
57 | let lineColor = buffer.readFullColor();
58 | let fillColor = buffer.readFullColor();
59 | let roundedRect = buffer.readBool();
60 | let cornerRadius: Array;
61 | if (roundedRect) {
62 | cornerRadius = [];
63 | for (i = 0; i < 4; i++)
64 | cornerRadius[i] = buffer.readFloat();
65 | }
66 |
67 | if (type == 1) {
68 | if (roundedRect)
69 | this._shape.drawRoundRect(lineSize, lineColor, fillColor, cornerRadius[0], cornerRadius[1], cornerRadius[2], cornerRadius[3]);
70 | else
71 | this._shape.drawRect(lineSize, lineColor, fillColor);
72 | }
73 | else if (type == 2) {
74 | this._shape.drawEllipse(lineSize, fillColor, lineColor, fillColor, 0, 360);
75 | }
76 | else if (type == 3) {
77 | cnt = buffer.readShort();
78 | let points: Array = [];
79 | points.length = cnt;
80 | for (i = 0; i < cnt; i++)
81 | points[i] = buffer.readFloat();
82 |
83 | this._shape.drawPolygon(points, fillColor, lineSize, lineColor);
84 | }
85 | else if (type == 4) {
86 | let sides = buffer.readShort();
87 | let startAngle = buffer.readFloat();
88 | cnt = buffer.readShort();
89 | let distances: Array;
90 | if (cnt > 0) {
91 | distances = [];
92 | for (i = 0; i < cnt; i++)
93 | distances[i] = buffer.readFloat();
94 | }
95 |
96 | this._shape.drawRegularPolygon(sides, lineSize, fillColor, lineColor, fillColor, startAngle, distances);
97 | }
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/core/text/RichTextField.ts:
--------------------------------------------------------------------------------
1 | import { HtmlElement, HtmlElementType } from "../../utils/html/HtmlElement";
2 | import { defaultContext } from "../../utils/html/HtmlPageContext";
3 | import { HtmlParseOptions } from "../../utils/html/HtmlParseOptions";
4 | import { IHtmlPageContext } from "../../utils/html/IHtmlPageContext";
5 | import { TextField } from "./TextField";
6 |
7 | export class RichTextField extends TextField {
8 | public htmlPageContext: IHtmlPageContext;
9 | public htmlParseOptions: HtmlParseOptions;
10 |
11 | public constructor() {
12 | super();
13 |
14 | this._touchDisabled = false;
15 | this.opaque = true;
16 | this.isRich = true;
17 |
18 | this.htmlPageContext = defaultContext;
19 | this.htmlParseOptions = new HtmlParseOptions();
20 | }
21 |
22 | public getHtmlElement(name: string): HtmlElement {
23 | let elements = this.htmlElements;
24 | let count = elements.length;
25 | for (let i = 0; i < count; i++) {
26 | let element = elements[i];
27 | if (name == element.name)
28 | return element;
29 | }
30 |
31 | return null;
32 | }
33 |
34 | public showHtmlObject(index: number, show: boolean): void {
35 | let element = this.htmlElements[index];
36 | if (element.htmlObject && element.type != HtmlElementType.Link) {
37 | //set hidden flag
38 | if (show)
39 | element.status &= 253; //~(1<<1)
40 | else
41 | element.status |= 2;
42 |
43 | if ((element.status & 3) == 0) //not (hidden and clipped)
44 | {
45 | if ((element.status & 4) == 0) //not added
46 | {
47 | element.status |= 4;
48 | element.htmlObject.add();
49 | }
50 | }
51 | else {
52 | if ((element.status & 4) != 0) //added
53 | {
54 | element.status &= 251;
55 | element.htmlObject.remove();
56 | }
57 | }
58 | }
59 | }
60 |
61 | public dispose() {
62 | this.cleanupObjects();
63 |
64 | super.dispose();
65 | }
66 |
67 | protected cleanupObjects() {
68 | let elements = this.htmlElements;
69 | let count = elements.length;
70 | for (let i = 0; i < count; i++) {
71 | let element = elements[i];
72 | if (element.htmlObject) {
73 | element.htmlObject.remove();
74 | this.htmlPageContext.freeObject(element.htmlObject);
75 | }
76 | }
77 | }
78 |
79 | protected refreshObjects() {
80 | let elements = this.htmlElements;
81 | let count = elements.length;
82 | for (let i = 0; i < count; i++) {
83 | let element = elements[i];
84 | if (element.htmlObject) {
85 | if ((element.status & 3) == 0) //not (hidden and clipped)
86 | {
87 | if ((element.status & 4) == 0) //not added
88 | {
89 | element.status |= 4;
90 | element.htmlObject.add();
91 | }
92 | }
93 | else {
94 | if ((element.status & 4) != 0) //added
95 | {
96 | element.status &= 251;
97 | element.htmlObject.remove();
98 | }
99 | }
100 | }
101 | }
102 | }
103 | }
--------------------------------------------------------------------------------
/src/core/text/BitmapFont.ts:
--------------------------------------------------------------------------------
1 | import { BaseFont, GlyphInfo } from "./BaseFont";
2 | import { TextFormat } from "./TextFormat";
3 | import { VertexBuffer } from "../mesh/VertexBuffer";
4 | import { Color4 } from "../../utils/Color";
5 | import { Rect } from "../../utils/Rect";
6 | import { NTexture } from "../NTexture";
7 | import { Vector2 } from "three";
8 |
9 | var s_rect = new Rect();
10 | var c_white = new Color4(0xFFFFFF, 1);
11 |
12 | export class BitmapFont implements BaseFont {
13 | public name: string;
14 | public version: number = 0;
15 | public mainTexture: NTexture;
16 | public size: number = 0;
17 | public glyphs: Record;
18 | public resizable: boolean;
19 | public hasChannel: boolean;
20 | public tint: boolean;
21 |
22 | private _color: Color4;
23 | private _scale: number;
24 | private _glyph: BMGlyph;
25 |
26 | constructor() {
27 | this.glyphs = {};
28 | this._color = new Color4();
29 | }
30 |
31 | public setFormat(format: TextFormat, fontSizeScale: number) {
32 | if (this.resizable)
33 | this._scale = format.size / this.size * fontSizeScale;
34 | else
35 | this._scale = fontSizeScale;
36 | this._color.setHex(format.color);
37 | }
38 |
39 | public prepareCharacters(text: string): void {
40 | }
41 |
42 | public getGlyph(ch: string, ret: GlyphInfo): boolean {
43 | if (ch == ' ') {
44 | ret.width = Math.round(this.size * this._scale / 2);
45 | ret.height = Math.round(this.size * this._scale);
46 | ret.baseline = ret.height;
47 | this._glyph = null;
48 | return true;
49 | }
50 | else if (this._glyph = this.glyphs[ch]) {
51 | ret.width = Math.round(this._glyph.advance * this._scale);
52 | ret.height = Math.round(this._glyph.lineHeight * this._scale);
53 | ret.baseline = ret.height;
54 | return true;
55 | }
56 | else {
57 | ret.width = 0;
58 | ret.height = 0;
59 | ret.baseline = 0;
60 | return false;
61 | }
62 | }
63 |
64 | public drawGlyph(x: number, y: number, vb: VertexBuffer): number {
65 | if (!this._glyph)
66 | return 0;
67 |
68 | let tx = x + this._glyph.x * this._scale;
69 | let ty = -y - this._glyph.y * this._scale;
70 | let bx = x + (this._glyph.x + this._glyph.width) * this._scale;
71 | let by = -y - (this._glyph.y + this._glyph.height) * this._scale;
72 | s_rect.setMinMax(tx, by, bx, ty);
73 | vb.addQuad(s_rect, this._glyph.uv, this.tint ? this._color : c_white);
74 | vb.addTriangles(-4);
75 | return 4;
76 | }
77 |
78 | public drawLine(x: number, y: number, width: number, fontSize: number, type: number, vb: VertexBuffer): number {
79 | return 0;
80 | }
81 |
82 | public getLineHeight(size: number): number {
83 | for (var key in this.glyphs) {
84 | let glyph = this.glyphs[key];
85 | if (this.resizable)
86 | return Math.round(glyph.lineHeight * size / this.size);
87 | else
88 | return glyph.lineHeight;
89 | }
90 |
91 | return 0;
92 | }
93 | }
94 |
95 | export class BMGlyph {
96 | public x: number = 0;
97 | public y: number = 0;
98 | public width: number = 0;
99 | public height: number = 0;
100 | public advance: number = 0;
101 | public lineHeight: number = 0;
102 | public channel: number = 0;
103 | public uv: Array = [new Vector2(), new Vector2(), new Vector2(), new Vector2()];
104 | }
105 |
--------------------------------------------------------------------------------
/src/utils/Rect.ts:
--------------------------------------------------------------------------------
1 | import { Vector2 } from "three";
2 |
3 | export class Rect {
4 | public x: number;
5 | public y: number;
6 | public width: number;
7 | public height: number;
8 |
9 | public constructor(x?: number, y?: number, width?: number, height?: number) {
10 | this.x = x || 0;
11 | this.y = y || 0;
12 | this.width = width || 0;
13 | this.height = height || 0;
14 | }
15 |
16 | public set(x: number, y: number, width: number, height: number) {
17 | this.x = x;
18 | this.y = y;
19 | this.width = width;
20 | this.height = height;
21 | }
22 |
23 | public setMinMax(xMin: number, yMin: number, xMax: number, yMax: number) {
24 | this.x = xMin;
25 | this.y = yMin;
26 | this.width = xMax - xMin;
27 | this.height = yMax - yMin;
28 | }
29 |
30 | public get position(): Vector2 {
31 | return new Vector2(this.x, this.y);
32 | }
33 |
34 | public get size(): Vector2 {
35 | return new Vector2(this.width, this.height);
36 | }
37 |
38 | public get xMin(): number {
39 | return this.x;
40 | }
41 |
42 | public set xMin(value: number) {
43 | let d = value - this.x;
44 | this.x = value;
45 | this.width -= d;
46 | }
47 |
48 | public get xMax(): number {
49 | return this.x + this.width;
50 | }
51 |
52 | public set xMax(value: number) {
53 | this.width = value - this.x;
54 | }
55 |
56 | public get yMin(): number {
57 | return this.y;
58 | }
59 |
60 | public set yMin(value: number) {
61 | let d = value - this.y;
62 | this.y = value;
63 | this.height -= d;
64 | }
65 |
66 | public get yMax(): number {
67 | return this.y + this.height;
68 | }
69 |
70 | public set yMax(value: number) {
71 | this.height = value - this.y;
72 | }
73 |
74 | public intersection(another: Rect): Rect {
75 | if (this.width == 0 || this.height == 0 || another.width == 0 || another.height == 0)
76 | return new Rect(0, 0, 0, 0);
77 |
78 | let left = this.x > another.x ? this.x : another.x;
79 | let right = this.xMax < another.xMax ? this.xMax : another.xMax;
80 | let top = this.y > another.y ? this.y : another.y;
81 | let bottom = this.yMax < another.yMax ? this.yMax : another.yMax;
82 |
83 | if (left > right || top > bottom)
84 | this.set(0, 0, 0, 0);
85 | else
86 | this.setMinMax(left, top, right, bottom);
87 |
88 | return this;
89 | }
90 |
91 | public union(another: Rect): Rect {
92 | if (another.width == 0 || another.height == 0)
93 | return this;
94 |
95 | if (this.width == 0 || this.height == 0) {
96 | this.copy(another);
97 | return this;
98 | }
99 |
100 | let x = Math.min(this.x, another.x);
101 | let y = Math.min(this.y, another.y);
102 | this.setMinMax(x, y, Math.max(this.xMax, another.xMax), Math.max(this.yMax, another.yMax));
103 |
104 | return this;
105 | }
106 |
107 | public extend(x: number, y: number): void {
108 | this.x -= x;
109 | this.y -= y;
110 | this.width += x * 2;
111 | this.height += y * 2;
112 | }
113 |
114 | public contains(x: number | Vector2, y?: number): boolean {
115 | if (x instanceof Vector2) {
116 | y = x.y;
117 | x = x.x;
118 | }
119 | return x >= this.x && x < this.x + this.width && y >= this.y && y < this.y + this.height;
120 | }
121 |
122 | public copy(source: Rect): void {
123 | this.set(source.x, source.y, source.width, source.height);
124 | }
125 |
126 | public clone(): Rect {
127 | return new Rect(this.x, this.y, this.width, this.height);
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/src/ui/GImage.ts:
--------------------------------------------------------------------------------
1 | import { Image } from "../core/Image";
2 | import { ObjectPropID } from "./FieldTypes";
3 | import { GObject } from "./GObject";
4 | import { PackageItem } from "./PackageItem";
5 | import { ByteBuffer } from "../utils/ByteBuffer";
6 |
7 | export class GImage extends GObject {
8 | private _image: Image;
9 | private _contentItem: PackageItem;
10 |
11 | constructor() {
12 | super();
13 | }
14 |
15 | public get color(): number {
16 | return this._image.graphics.color;
17 | }
18 |
19 | public set color(value: number) {
20 | if (this._image.graphics.color != value) {
21 | this._image.graphics.color = value;
22 | this.updateGear(4);
23 | }
24 | }
25 |
26 | public get flip(): number {
27 | return this._image.graphics.flip;
28 | }
29 |
30 | public set flip(value: number) {
31 | this._image.graphics.flip = value;
32 | }
33 |
34 | public get fillMethod(): number {
35 | return this._image.fillMethod;
36 | }
37 |
38 | public set fillMethod(value: number) {
39 | this._image.fillMethod = value;
40 | }
41 |
42 | public get fillOrigin(): number {
43 | return this._image.fillOrigin;
44 | }
45 |
46 | public set fillOrigin(value: number) {
47 | this._image.fillOrigin = value;
48 | }
49 |
50 | public get fillClockwise(): boolean {
51 | return this._image.fillClockwise;
52 | }
53 |
54 | public set fillClockwise(value: boolean) {
55 | this._image.fillClockwise = value;
56 | }
57 |
58 | public get fillAmount(): number {
59 | return this._image.fillAmount;
60 | }
61 |
62 | public set fillAmount(value: number) {
63 | this._image.fillAmount = value;
64 | }
65 |
66 | protected createDisplayObject(): void {
67 | this._displayObject = this._image = new Image();
68 | }
69 |
70 | protected handleSizeChanged(): void {
71 | this._image.width = this._width;
72 | this._image.height = this._height;
73 | }
74 |
75 | public constructFromResource(): void {
76 | this._contentItem = this.packageItem.getBranch();
77 |
78 | this.sourceWidth = this._contentItem.width;
79 | this.sourceHeight = this._contentItem.height;
80 | this.initWidth = this.sourceWidth;
81 | this.initHeight = this.sourceHeight;
82 |
83 | this._contentItem = this._contentItem.getHighResolution();
84 | this._contentItem.load();
85 |
86 | this._image.scale9Grid = this._contentItem.scale9Grid;
87 | this._image.scaleByTile = this._contentItem.scaleByTile;
88 | this._image.tileGridIndice = this._contentItem.tileGridIndice;
89 | this._image.texture = this._contentItem.texture;
90 |
91 | this.setSize(this.sourceWidth, this.sourceHeight);
92 | }
93 |
94 | public getProp(index: number): any {
95 | if (index == ObjectPropID.Color)
96 | return this.color;
97 | else
98 | return super.getProp(index);
99 | }
100 |
101 | public setProp(index: number, value: any): void {
102 | if (index == ObjectPropID.Color)
103 | this.color = value;
104 | else
105 | super.setProp(index, value);
106 | }
107 |
108 | public setup_beforeAdd(buffer: ByteBuffer, beginPos: number): void {
109 | super.setup_beforeAdd(buffer, beginPos);
110 |
111 | buffer.seek(beginPos, 5);
112 |
113 | if (buffer.readBool())
114 | this.color = buffer.readColor();
115 | this.flip = buffer.readByte();
116 | this._image.fillMethod = buffer.readByte();
117 | if (this._image.fillMethod != 0) {
118 | this._image.fillOrigin = buffer.readByte();
119 | this._image.fillClockwise = buffer.readBool();
120 | this._image.fillAmount = buffer.readFloat();
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/gears/GearBase.ts:
--------------------------------------------------------------------------------
1 | import { Controller } from "../ui/Controller";
2 | import { GObject, constructingDepth } from "../ui/GObject";
3 | import { EaseType } from "../tween/EaseType";
4 | import { GTweener } from "../tween/GTweener";
5 | import { ByteBuffer } from "../utils/ByteBuffer";
6 |
7 | export class GearBase {
8 | public static disableAllTweenEffect?: boolean;
9 |
10 | public _owner: GObject;
11 | protected _controller: Controller;
12 | protected _tweenConfig: GearTweenConfig;
13 |
14 | public dispose(): void {
15 | if (this._tweenConfig && this._tweenConfig._tweener) {
16 | this._tweenConfig._tweener.kill();
17 | this._tweenConfig._tweener = null;
18 | }
19 | }
20 |
21 | public get controller(): Controller {
22 | return this._controller;
23 | }
24 |
25 | public set controller(val: Controller) {
26 | if (val != this._controller) {
27 | this._controller = val;
28 | if (this._controller)
29 | this.init();
30 | }
31 | }
32 |
33 | public get tweenConfig(): GearTweenConfig {
34 | if (!this._tweenConfig)
35 | this._tweenConfig = new GearTweenConfig();
36 | return this._tweenConfig;
37 | }
38 |
39 | protected get allowTween(): boolean {
40 | return this._tweenConfig && this._tweenConfig.tween && constructingDepth.n == 0 && !GearBase.disableAllTweenEffect;
41 | }
42 |
43 | public setup(buffer: ByteBuffer): void {
44 | this._controller = this._owner.parent.getControllerAt(buffer.readShort());
45 | this.init();
46 |
47 | var i: number;
48 | var page: string;
49 | var cnt: number = buffer.readShort();
50 |
51 | if ("pages" in this) {
52 | (this).pages = buffer.readSArray(cnt);
53 | }
54 | else {
55 | for (i = 0; i < cnt; i++) {
56 | page = buffer.readS();
57 | if (page == null)
58 | continue;
59 |
60 | this.addStatus(page, buffer);
61 | }
62 |
63 | if (buffer.readBool())
64 | this.addStatus(null, buffer);
65 | }
66 |
67 | if (buffer.readBool()) {
68 | this._tweenConfig = new GearTweenConfig();
69 | this._tweenConfig.easeType = buffer.readByte();
70 | this._tweenConfig.duration = buffer.readFloat();
71 | this._tweenConfig.delay = buffer.readFloat();
72 | }
73 |
74 | if (buffer.version >= 2) {
75 | if ("positionsInPercent" in this) {
76 | if (buffer.readBool()) {
77 | (this).positionsInPercent = true;
78 | for (i = 0; i < cnt; i++) {
79 | page = buffer.readS();
80 | if (page == null)
81 | continue;
82 |
83 | (this).addExtStatus(page, buffer);
84 | }
85 |
86 | if (buffer.readBool())
87 | (this).addExtStatus(null, buffer);
88 | }
89 | }
90 | else if ("condition" in this)
91 | (this).condition = buffer.readByte();
92 | }
93 | }
94 |
95 | public updateFromRelations(dx: number, dy: number): void {
96 |
97 | }
98 |
99 | protected addStatus(pageId: string, buffer: ByteBuffer): void {
100 |
101 | }
102 |
103 | protected init(): void {
104 |
105 | }
106 |
107 | public apply(): void {
108 | }
109 |
110 | public updateState(): void {
111 | }
112 | }
113 |
114 | export class GearTweenConfig {
115 | public tween: boolean;
116 | public easeType: number;
117 | public duration: number;
118 | public delay: number;
119 |
120 | public _displayLockToken: number;
121 | public _tweener: GTweener;
122 |
123 | constructor() {
124 | this.tween = true;
125 | this.easeType = EaseType.QuadOut;
126 | this.duration = 0.3;
127 | this.delay = 0;
128 | }
129 | }
130 |
131 | export interface IGearXY {
132 |
133 | }
--------------------------------------------------------------------------------
/src/utils/Timers.ts:
--------------------------------------------------------------------------------
1 | import { Pool } from "./Pool";
2 |
3 | export class Timers {
4 | public static deltaTime: number = 0;
5 | public static time: number = 0;
6 | public static frameCount: number = 0;
7 |
8 | public static add(delayInMiniseconds: number, repeat: number, callback: Function, target?: any, callbackParam?: any): void {
9 | let item: TimerItem;
10 | let index = _items.findIndex(e => e.target == target && e.callback == callback);
11 | if (index != -1)
12 | item = _items[index];
13 | else {
14 | item = _pool.borrow();
15 | item.callback = callback;
16 | item.target = target;
17 | _items.push(item);
18 | }
19 | item.delay = delayInMiniseconds;
20 | item.counter = 0;
21 | item.repeat = repeat;
22 | item.param = callbackParam;
23 | item.end = false;
24 | }
25 |
26 | public static callLater(callback: Function, target?: any, callbackParam?: any): void {
27 | this.add(0, 1, callback, target, callbackParam);
28 | }
29 |
30 | public static callDelay(delay: number, callback: Function, target?: any, callbackParam?: any): void {
31 | this.add(delay, 1, callback, target, callbackParam);
32 | }
33 |
34 | public static addUpdate(callback: Function, target?: any, callbackParam?: any): void {
35 | this.add(0, 0, callback, target, callbackParam);
36 | }
37 |
38 | public static exists(callback: Function, target?: any): boolean {
39 | return _items.findIndex(e => e.target == target && e.callback == callback) != -1;
40 | }
41 |
42 | public static remove(callback: Function, target?: any): void {
43 | let index = _items.findIndex(e => e.target == target && e.callback == callback);
44 | if (index != -1) {
45 | let item = _items[index];
46 | _items.splice(index, 1);
47 | if (index < _enumI)
48 | _enumI--;
49 | _enumCount--;
50 |
51 | _pool.returns(item);
52 | }
53 | }
54 | }
55 |
56 |
57 | class TimerItem {
58 | public delay: number = 0;
59 | public counter: number = 0;
60 | public repeat: number = 0;
61 | public callback: Function;
62 | public target: any;
63 | public param: any;
64 | public end: boolean;
65 |
66 | public constructor() {
67 | }
68 |
69 | public advance(elapsed: number): boolean {
70 | this.counter += elapsed;
71 | if (this.counter >= this.delay) {
72 | this.counter -= this.delay;
73 | if (this.counter > this.delay)
74 | this.counter = this.delay;
75 |
76 | if (this.repeat > 0) {
77 | this.repeat--;
78 | if (this.repeat == 0)
79 | this.end = true;
80 | }
81 |
82 | return true;
83 | }
84 | else
85 | return false;
86 | }
87 |
88 | public reset(): void {
89 | this.callback = null;
90 | this.target = null;
91 | this.param = null;
92 | }
93 | }
94 |
95 | var _items: Array = new Array();
96 | var _pool: Pool = new Pool(TimerItem, e => e.reset());
97 |
98 | var _enumI: number = 0;
99 | var _enumCount: number = 0;
100 | var _lastTime: number = 0;
101 |
102 | requestAnimationFrame(__timer);
103 |
104 | function __timer(timeStamp: number): boolean {
105 | requestAnimationFrame(__timer);
106 |
107 | Timers.frameCount++;
108 | Timers.time = timeStamp;
109 | let deltaTime = timeStamp - _lastTime;
110 | Timers.deltaTime = deltaTime;
111 | _lastTime = timeStamp;
112 |
113 | _enumI = 0;
114 | _enumCount = _items.length;
115 |
116 | while (_enumI < _enumCount) {
117 | var item: TimerItem = _items[_enumI];
118 | _enumI++;
119 |
120 | if (item.advance(deltaTime)) {
121 | if (item.end) {
122 | _enumI--;
123 | _enumCount--;
124 | _items.splice(_enumI, 1);
125 | }
126 |
127 | item.callback.call(item.target, item.param);
128 |
129 | if (item.end)
130 | _pool.returns(item);
131 | }
132 | }
133 |
134 | return false;
135 | }
--------------------------------------------------------------------------------
/src/tween/TweenManager.ts:
--------------------------------------------------------------------------------
1 | import { Timers } from "../utils/Timers";
2 | import { GTweener } from "./GTweener";
3 | import { Pool } from "../utils/Pool";
4 |
5 | export class TweenManager {
6 |
7 | public static createTween(): GTweener {
8 | if (!_inited) {
9 | Timers.addUpdate(TweenManager.update);
10 | _inited = true;
11 | }
12 |
13 | var tweener: GTweener = _tweenerPool.borrow();
14 | _activeTweens[_totalActiveTweens++] = tweener;
15 |
16 | return tweener;
17 | }
18 |
19 | public static isTweening(target: any, propType?: any): boolean {
20 | if (target == null)
21 | return false;
22 |
23 | var anyType: boolean = !propType;
24 | for (var i: number = 0; i < _totalActiveTweens; i++) {
25 | var tweener: GTweener = _activeTweens[i];
26 | if (tweener && tweener.target == target && !tweener._killed
27 | && (anyType || tweener._propType == propType))
28 | return true;
29 | }
30 |
31 | return false;
32 | }
33 |
34 | public static killTweens(target: any, completed?: boolean, propType?: any): boolean {
35 | if (target == null)
36 | return false;
37 |
38 | var flag: boolean = false;
39 | var cnt: number = _totalActiveTweens;
40 | var anyType: boolean = !propType;
41 | for (var i: number = 0; i < cnt; i++) {
42 | var tweener: GTweener = _activeTweens[i];
43 | if (tweener && tweener.target == target && !tweener._killed
44 | && (anyType || tweener._propType == propType)) {
45 | tweener.kill(completed);
46 | flag = true;
47 | }
48 | }
49 |
50 | return flag;
51 | }
52 |
53 | public static getTween(target: any, propType?: any): GTweener {
54 | if (target == null)
55 | return null;
56 |
57 | var cnt: number = _totalActiveTweens;
58 | var anyType: boolean = !propType;
59 | for (var i: number = 0; i < cnt; i++) {
60 | var tweener: GTweener = _activeTweens[i];
61 | if (tweener && tweener.target == target && !tweener._killed
62 | && (anyType || tweener._propType == propType)) {
63 | return tweener;
64 | }
65 | }
66 |
67 | return null;
68 | }
69 |
70 | public static update(): void {
71 | var dt: number = Timers.deltaTime / 1000;
72 |
73 | var cnt: number = _totalActiveTweens;
74 | var freePosStart: number = -1;
75 | for (var i: number = 0; i < cnt; i++) {
76 | var tweener: GTweener = _activeTweens[i];
77 | if (tweener == null) {
78 | if (freePosStart == -1)
79 | freePosStart = i;
80 | }
81 | else if (tweener._killed) {
82 | _tweenerPool.returns(tweener);
83 | _activeTweens[i] = null;
84 |
85 | if (freePosStart == -1)
86 | freePosStart = i;
87 | }
88 | else {
89 | if (tweener._target && ('isDisposed' in tweener._target) && tweener._target.isDisposed)
90 | tweener._killed = true;
91 | else if (!tweener._paused)
92 | tweener._update(dt);
93 |
94 | if (freePosStart != -1) {
95 | _activeTweens[freePosStart] = tweener;
96 | _activeTweens[i] = null;
97 | freePosStart++;
98 | }
99 | }
100 | }
101 |
102 | if (freePosStart >= 0) {
103 | if (_totalActiveTweens != cnt) //new tweens added
104 | {
105 | var j: number = cnt;
106 | cnt = _totalActiveTweens - cnt;
107 | for (i = 0; i < cnt; i++)
108 | _activeTweens[freePosStart++] = _activeTweens[j++];
109 | }
110 | _totalActiveTweens = freePosStart;
111 | }
112 | }
113 | }
114 |
115 | var _activeTweens: GTweener[] = new Array();
116 | var _tweenerPool: Pool = new Pool(GTweener, e => e._init(), e => e._reset());
117 | var _totalActiveTweens: number = 0;
118 | var _inited: boolean = false;
--------------------------------------------------------------------------------
/src/gears/GearLook.ts:
--------------------------------------------------------------------------------
1 | import { GTween } from "../tween/GTween";
2 | import { GTweener } from "../tween/GTweener";
3 | import { ByteBuffer } from "../utils/ByteBuffer";
4 | import { GearBase } from "./GearBase";
5 |
6 | export class GearLook extends GearBase {
7 | private _storage: Record;
8 | private _default: GearLookValue;
9 |
10 | protected init(): void {
11 | this._default = {
12 | alpha: this._owner.alpha,
13 | rotation: this._owner.rotation,
14 | grayed: this._owner.grayed,
15 | touchable: this._owner.touchable
16 | };
17 | this._storage = {};
18 | }
19 |
20 | protected addStatus(pageId: string, buffer: ByteBuffer): void {
21 | var gv: GearLookValue;
22 | if (!pageId)
23 | gv = this._default;
24 | else {
25 | gv = {};
26 | this._storage[pageId] = gv;
27 | }
28 |
29 | gv.alpha = buffer.readFloat();
30 | gv.rotation = buffer.readFloat();
31 | gv.grayed = buffer.readBool();
32 | gv.touchable = buffer.readBool();
33 | }
34 |
35 | public apply(): void {
36 | var gv: GearLookValue = this._storage[this._controller.selectedPageId] || this._default;
37 |
38 | if (this.allowTween) {
39 | this._owner._gearLocked = true;
40 | this._owner.grayed = gv.grayed;
41 | this._owner.touchable = gv.touchable;
42 | this._owner._gearLocked = false;
43 |
44 | if (this._tweenConfig._tweener) {
45 | if (this._tweenConfig._tweener.endValue.x != gv.alpha || this._tweenConfig._tweener.endValue.y != gv.rotation) {
46 | this._tweenConfig._tweener.kill(true);
47 | this._tweenConfig._tweener = null;
48 | }
49 | else
50 | return;
51 | }
52 |
53 | var a: boolean = gv.alpha != this._owner.alpha;
54 | var b: boolean = gv.rotation != this._owner.rotation;
55 | if (a || b) {
56 | if (this._owner.checkGearController(0, this._controller))
57 | this._tweenConfig._displayLockToken = this._owner.addDisplayLock();
58 |
59 | this._tweenConfig._tweener = GTween.to2(this._owner.alpha, this._owner.rotation, gv.alpha, gv.rotation, this._tweenConfig.duration)
60 | .setDelay(this._tweenConfig.delay)
61 | .setEase(this._tweenConfig.easeType)
62 | .setUserData((a ? 1 : 0) + (b ? 2 : 0))
63 | .setTarget(this)
64 | .onUpdate(this.__tweenUpdate, this)
65 | .onComplete(this.__tweenComplete, this);
66 | }
67 | }
68 | else {
69 | this._owner._gearLocked = true;
70 | this._owner.grayed = gv.grayed;
71 | this._owner.alpha = gv.alpha;
72 | this._owner.rotation = gv.rotation;
73 | this._owner.touchable = gv.touchable;
74 | this._owner._gearLocked = false;
75 | }
76 | }
77 |
78 | private __tweenUpdate(tweener: GTweener): void {
79 | var flag: number = tweener.userData;
80 | this._owner._gearLocked = true;
81 | if ((flag & 1) != 0)
82 | this._owner.alpha = tweener.value.x;
83 | if ((flag & 2) != 0)
84 | this._owner.rotation = tweener.value.y;
85 | this._owner._gearLocked = false;
86 | }
87 |
88 | private __tweenComplete(): void {
89 | if (this._tweenConfig._displayLockToken != 0) {
90 | this._owner.releaseDisplayLock(this._tweenConfig._displayLockToken);
91 | this._tweenConfig._displayLockToken = 0;
92 | }
93 | this._tweenConfig._tweener = null;
94 | }
95 |
96 | public updateState(): void {
97 | var gv: GearLookValue = this._storage[this._controller.selectedPageId];
98 | if (!gv) {
99 | gv = {};
100 | this._storage[this._controller.selectedPageId] = gv;
101 | }
102 |
103 | gv.alpha = this._owner.alpha;
104 | gv.rotation = this._owner.rotation;
105 | gv.grayed = this._owner.grayed;
106 | gv.touchable = this._owner.touchable;
107 | }
108 | }
109 |
110 | interface GearLookValue {
111 | alpha?: number;
112 | rotation?: number;
113 | grayed?: boolean;
114 | touchable?: boolean;
115 | }
116 |
--------------------------------------------------------------------------------
/src/utils/xml/XML.ts:
--------------------------------------------------------------------------------
1 | import { XMLIterator, XMLTagType } from "./XMLIterator";
2 | import { XMLUtils } from "./XMLUtils";
3 |
4 | export class XML {
5 | public name: string;
6 | public text: string;
7 |
8 | private _attributes: Record;
9 | private _children: Array;
10 |
11 | public constructor(XmlString?: string) {
12 | if (XmlString)
13 | this.parse(XmlString);
14 | }
15 |
16 | public get attributes(): Record {
17 | if (!this._attributes)
18 | this._attributes = {};
19 | return this._attributes;
20 | }
21 |
22 | public getAttrString(attrName: string, defValue?: string) {
23 | return XMLUtils.getString(this._attributes, attrName, defValue);
24 | }
25 |
26 | public getAttrInt(attrName: string, defValue?: number): number {
27 | return XMLUtils.getInt(this._attributes, attrName, defValue);
28 | }
29 |
30 | public getAttrFloat(attrName: string, defValue?: number): number {
31 | return XMLUtils.getFloat(this._attributes, attrName, defValue);
32 | }
33 |
34 | public getAttrBool(attrName: string, defValue?: boolean): boolean {
35 | return XMLUtils.getBool(this._attributes, attrName, defValue);
36 | }
37 |
38 | public getAttrColor(attrName: string, defValue?: number): number {
39 | return XMLUtils.getColor(this._attributes, attrName, defValue);
40 | }
41 |
42 | public setAttribute(attrName: string, attrValue: string) {
43 | if (!this._attributes)
44 | this._attributes = {};
45 |
46 | this._attributes[attrName] = attrValue;
47 | }
48 |
49 | public getNode(selector: string): XML {
50 | if (!this._children)
51 | return null;
52 | else
53 | this._children.find(value => {
54 | return value.name == selector;
55 | });
56 | }
57 |
58 | public elements(selector?: string): Array {
59 | if (!this._children)
60 | this._children = new Array();
61 | if (selector)
62 | return this._children.filter(value => {
63 | return value.name == selector;
64 | });
65 | else
66 | return this._children;
67 | }
68 |
69 | public parse(aSource: string) {
70 | this.reset();
71 |
72 | let lastOpenNode: XML;
73 | let nodeStack: Array = new Array();
74 |
75 | XMLIterator.begin(aSource);
76 | while (XMLIterator.nextTag()) {
77 | if (XMLIterator.tagType == XMLTagType.Start || XMLIterator.tagType == XMLTagType.Void) {
78 | let childNode: XML;
79 | if (lastOpenNode)
80 | childNode = new XML();
81 | else {
82 | if (this.name != null) {
83 | this.reset();
84 | throw new Error("Invalid xml format - no root node.");
85 | }
86 | childNode = this;
87 | }
88 |
89 | childNode.name = XMLIterator.tagName;
90 | childNode._attributes = XMLIterator.getAttributes(childNode._attributes);
91 |
92 | if (lastOpenNode) {
93 | if (XMLIterator.tagType != XMLTagType.Void)
94 | nodeStack.push(lastOpenNode);
95 | if (lastOpenNode._children == null)
96 | lastOpenNode._children = new Array();
97 | lastOpenNode._children.push(childNode);
98 | }
99 | if (XMLIterator.tagType != XMLTagType.Void)
100 | lastOpenNode = childNode;
101 | }
102 | else if (XMLIterator.tagType == XMLTagType.End) {
103 | if (lastOpenNode == null || lastOpenNode.name != XMLIterator.tagName) {
104 | this.reset();
105 | throw new Error("Invalid xml format - <" + XMLIterator.tagName + "> dismatched.");
106 | }
107 |
108 | if (lastOpenNode._children == null || lastOpenNode._children.length == 0) {
109 | lastOpenNode.text = XMLIterator.getText();
110 | }
111 |
112 | if (nodeStack.length > 0)
113 | lastOpenNode = nodeStack.pop();
114 | else
115 | lastOpenNode = null;
116 | }
117 | }
118 | }
119 |
120 | public reset() {
121 | this._attributes = null;
122 | if (this._children != null)
123 | this._children.length == 0;
124 | this.text = null;
125 | }
126 | }
--------------------------------------------------------------------------------
/src/ui/GMovieClip.ts:
--------------------------------------------------------------------------------
1 | import { MovieClip } from "../core/MovieClip";
2 | import { GObject } from "./GObject";
3 | import { ObjectPropID } from "./FieldTypes";
4 | import { PackageItem } from "./PackageItem";
5 | import { ByteBuffer } from "../utils/ByteBuffer";
6 |
7 | export class GMovieClip extends GObject {
8 | private _movieClip: MovieClip;
9 |
10 | constructor() {
11 | super();
12 | }
13 |
14 | public get color(): number {
15 | return this._movieClip.graphics.color;
16 | }
17 |
18 | public set color(value: number) {
19 | this._movieClip.graphics.color = value;
20 | }
21 |
22 | protected createDisplayObject(): void {
23 | this._displayObject = this._movieClip = new MovieClip();
24 | }
25 |
26 | public get playing(): boolean {
27 | return this._movieClip.playing;
28 | }
29 |
30 | public set playing(value: boolean) {
31 | if (this._movieClip.playing != value) {
32 | this._movieClip.playing = value;
33 | this.updateGear(5);
34 | }
35 | }
36 |
37 | public get frame(): number {
38 | return this._movieClip.frame;
39 | }
40 |
41 | public set frame(value: number) {
42 | if (this._movieClip.frame != value) {
43 | this._movieClip.frame = value;
44 | this.updateGear(5);
45 | }
46 | }
47 |
48 | public get timeScale(): number {
49 | return this._movieClip.timeScale;
50 | }
51 |
52 | public set timeScale(value: number) {
53 | this._movieClip.timeScale = value;
54 | }
55 |
56 | public rewind(): void {
57 | this._movieClip.rewind();
58 | }
59 |
60 | public syncStatus(anotherMc: GMovieClip): void {
61 | this._movieClip.syncStatus(anotherMc._movieClip);
62 | }
63 |
64 | public advance(timeInMiniseconds: number): void {
65 | this._movieClip.advance(timeInMiniseconds);
66 | }
67 |
68 | //从start帧开始,播放到end帧(-1表示结尾),重复times次(0表示无限循环),循环结束后,停止在endAt帧(-1表示参数end)
69 | public setPlaySettings(start?: number, end?: number, times?: number, endAt?: number): void {
70 | this._movieClip.setPlaySettings(start, end, times, endAt);
71 | }
72 |
73 | public getProp(index: number): any {
74 | switch (index) {
75 | case ObjectPropID.Color:
76 | return this.color;
77 | case ObjectPropID.Playing:
78 | return this.playing;
79 | case ObjectPropID.Frame:
80 | return this.frame;
81 | case ObjectPropID.TimeScale:
82 | return this.timeScale;
83 | default:
84 | return super.getProp(index);
85 | }
86 | }
87 |
88 | public setProp(index: number, value: any): void {
89 | switch (index) {
90 | case ObjectPropID.Color:
91 | this.color = value;
92 | break;
93 | case ObjectPropID.Playing:
94 | this.playing = value;
95 | break;
96 | case ObjectPropID.Frame:
97 | this.frame = value;
98 | break;
99 | case ObjectPropID.TimeScale:
100 | this.timeScale = value;
101 | break;
102 | case ObjectPropID.DeltaTime:
103 | this.advance(value);
104 | break;
105 | default:
106 | super.setProp(index, value);
107 | break;
108 | }
109 | }
110 |
111 | public constructFromResource(): void {
112 | var displayItem: PackageItem = this.packageItem.getBranch();
113 |
114 | this.sourceWidth = displayItem.width;
115 | this.sourceHeight = displayItem.height;
116 | this.initWidth = this.sourceWidth;
117 | this.initHeight = this.sourceHeight;
118 |
119 | this.setSize(this.sourceWidth, this.sourceHeight);
120 |
121 | displayItem = displayItem.getHighResolution();
122 | displayItem.load();
123 |
124 | this._movieClip.interval = displayItem.interval;
125 | this._movieClip.swing = displayItem.swing;
126 | this._movieClip.repeatDelay = displayItem.repeatDelay;
127 | this._movieClip.frames = displayItem.frames;
128 | }
129 |
130 | public setup_beforeAdd(buffer: ByteBuffer, beginPos: number): void {
131 | super.setup_beforeAdd(buffer, beginPos);
132 |
133 | buffer.seek(beginPos, 5);
134 |
135 | if (buffer.readBool())
136 | this.color = buffer.readColor();
137 | this._movieClip.graphics.flip = buffer.readByte(); //flip
138 | this._movieClip.frame = buffer.readInt();
139 | this._movieClip.playing = buffer.readBool();
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/gears/GearXY.ts:
--------------------------------------------------------------------------------
1 | import { GTween } from "../tween/GTween";
2 | import { GTweener } from "../tween/GTweener";
3 | import { ByteBuffer } from "../utils/ByteBuffer";
4 | import { GearBase } from "./GearBase";
5 |
6 | export class GearXY extends GearBase {
7 | public positionsInPercent: boolean;
8 |
9 | private _storage: Record;
10 | private _default: GearXYValue;
11 |
12 | protected init(): void {
13 | this._default = {
14 | x: this._owner.x,
15 | y: this._owner.y,
16 | px: this._owner.x / this._owner.parent.width,
17 | py: this._owner.y / this._owner.parent.height
18 | };
19 | this._storage = {};
20 | }
21 |
22 | protected addStatus(pageId: string, buffer: ByteBuffer): void {
23 | var gv: GearXYValue;
24 | if (!pageId)
25 | gv = this._default;
26 | else {
27 | gv = {};
28 | this._storage[pageId] = gv;
29 | }
30 | gv.x = buffer.readInt();
31 | gv.y = buffer.readInt();
32 | }
33 |
34 | public addExtStatus(pageId: string, buffer: ByteBuffer): void {
35 | var gv: GearXYValue;
36 | if (!pageId)
37 | gv = this._default;
38 | else
39 | gv = this._storage[pageId];
40 | gv.px = buffer.readFloat();
41 | gv.py = buffer.readFloat();
42 | }
43 |
44 | public apply(): void {
45 | var pt: GearXYValue = this._storage[this._controller.selectedPageId] || this._default;
46 | var ex: number;
47 | var ey: number;
48 |
49 | if (this.positionsInPercent && this._owner.parent) {
50 | ex = pt.px * this._owner.parent.width;
51 | ey = pt.py * this._owner.parent.height;
52 | }
53 | else {
54 | ex = pt.x;
55 | ey = pt.y;
56 | }
57 |
58 | if (this.allowTween) {
59 | if (this._tweenConfig._tweener) {
60 | if (this._tweenConfig._tweener.endValue.x != ex || this._tweenConfig._tweener.endValue.y != ey) {
61 | this._tweenConfig._tweener.kill(true);
62 | this._tweenConfig._tweener = null;
63 | }
64 | else
65 | return;
66 | }
67 |
68 | var ox: number = this._owner.x;
69 | var oy: number = this._owner.y;
70 |
71 | if (ox != ex || oy != ey) {
72 | if (this._owner.checkGearController(0, this._controller))
73 | this._tweenConfig._displayLockToken = this._owner.addDisplayLock();
74 |
75 | this._tweenConfig._tweener = GTween.to2(ox, oy, ex, ey, this._tweenConfig.duration)
76 | .setDelay(this._tweenConfig.delay)
77 | .setEase(this._tweenConfig.easeType)
78 | .setTarget(this)
79 | .onUpdate(this.__tweenUpdate, this)
80 | .onComplete(this.__tweenComplete, this);
81 | }
82 | }
83 | else {
84 | this._owner._gearLocked = true;
85 | this._owner.setPosition(ex, ey);
86 | this._owner._gearLocked = false;
87 | }
88 | }
89 |
90 | private __tweenUpdate(tweener: GTweener): void {
91 | this._owner._gearLocked = true;
92 | this._owner.setPosition(tweener.value.x, tweener.value.y);
93 | this._owner._gearLocked = false;
94 | }
95 |
96 | private __tweenComplete(): void {
97 | if (this._tweenConfig._displayLockToken != 0) {
98 | this._owner.releaseDisplayLock(this._tweenConfig._displayLockToken);
99 | this._tweenConfig._displayLockToken = 0;
100 | }
101 | this._tweenConfig._tweener = null;
102 | }
103 |
104 | public updateState(): void {
105 | var pt: GearXYValue = this._storage[this._controller.selectedPageId];
106 | if (!pt) {
107 | pt = {};
108 | this._storage[this._controller.selectedPageId] = pt;
109 | }
110 |
111 | pt.x = this._owner.x;
112 | pt.y = this._owner.y;
113 | pt.px = this._owner.x / this._owner.parent.width;
114 | pt.py = this._owner.y / this._owner.parent.height;
115 | }
116 |
117 | public updateFromRelations(dx: number, dy: number): void {
118 | if (this._controller == null || this._storage == null || this.positionsInPercent)
119 | return;
120 |
121 | for (var key in this._storage) {
122 | var pt: GearXYValue = this._storage[key];
123 | pt.x += dx;
124 | pt.y += dy;
125 | }
126 | this._default.x += dx;
127 | this._default.y += dy;
128 |
129 | this.updateState();
130 | }
131 | }
132 |
133 | interface GearXYValue {
134 | x?: number,
135 | y?: number,
136 | px?: number;
137 | py?: number
138 | }
--------------------------------------------------------------------------------
/src/core/NTexture.ts:
--------------------------------------------------------------------------------
1 | import { Rect } from "../utils/Rect"
2 | import { Texture, Vector2 } from "three";
3 |
4 | export class NTexture {
5 | public uvRect: Rect;
6 | public rotated: boolean;
7 |
8 | public region: Rect;
9 | public offset: Vector2;
10 | public originalSize: Vector2;
11 |
12 | private _root: NTexture;
13 | private _nativeTexture: Texture;
14 |
15 | public constructor(texture?: Texture, xScale?: number, yScale?: number) {
16 | xScale = xScale || 1;
17 | yScale = yScale || 1;
18 | this._nativeTexture = texture;
19 | this._root = this;
20 | this.uvRect = new Rect(0, 0, xScale, yScale);
21 | if (yScale < 0) {
22 | this.uvRect.y = -yScale;
23 | this.uvRect.height = yScale;
24 | }
25 | if (xScale < 0) {
26 | this.uvRect.x = -xScale;
27 | this.uvRect.width = xScale;
28 | }
29 | this.originalSize = texture ? new Vector2(texture.image.width, texture.image.height) : new Vector2(2, 2);
30 | this.region = new Rect(0, 0, this.originalSize.x, this.originalSize.y);
31 | this.offset = new Vector2(0, 0);
32 | }
33 |
34 | public createSubTexture(region: Rect, rotated?: boolean, offset?: Vector2, originalSize?: Vector2): NTexture {
35 | let nt = new NTexture();
36 | nt._root = this;
37 | nt.rotated = rotated || false;
38 | nt.region.copy(region);
39 | nt.region.x += this.region.x;
40 | nt.region.y += this.region.y;
41 | nt.uvRect.set(nt.region.x * this.uvRect.width / this.width, 1 - nt.region.yMax * this.uvRect.height / this.height,
42 | nt.region.width * this.uvRect.width / this.width, nt.region.height * this.uvRect.height / this.height);
43 | if (rotated) {
44 | let tmp: number = nt.region.width;
45 | nt.region.width = nt.region.height;
46 | nt.region.height = tmp;
47 |
48 | tmp = nt.uvRect.width;
49 | nt.uvRect.width = nt.uvRect.height;
50 | nt.uvRect.height = tmp;
51 | }
52 | if (originalSize)
53 | nt.originalSize.copy(originalSize);
54 | else
55 | nt.originalSize.set(nt.region.width, nt.region.height);
56 | if (offset)
57 | nt.offset.copy(offset);
58 | return nt;
59 | }
60 |
61 | public get width(): number {
62 | return this.region.width;
63 | }
64 |
65 | public get height(): number {
66 | return this.region.height;
67 | }
68 |
69 | public get nativeTexture(): Texture {
70 | return this._root == this ? this._nativeTexture : this._root.nativeTexture;
71 | }
72 |
73 | public getDrawRect(drawRect: Rect): Rect {
74 | if (this.originalSize.x == this.region.width && this.originalSize.y == this.region.height)
75 | return drawRect;
76 |
77 | let sx = drawRect.width / this.originalSize.x;
78 | let sy = drawRect.height / this.originalSize.y;
79 | drawRect.x = this.offset.x * sx;
80 | drawRect.y = this.offset.y * sy;
81 | drawRect.width = this.region.width * sx;
82 | drawRect.height = this.region.height * sy;
83 |
84 | return drawRect;
85 | }
86 |
87 | public getUV(uv: Array) {
88 | uv[0] = this.uvRect.position;
89 | uv[1] = new Vector2(this.uvRect.x, this.uvRect.yMax);
90 | uv[2] = new Vector2(this.uvRect.xMax, this.uvRect.yMax);
91 | uv[3] = new Vector2(this.uvRect.xMax, this.uvRect.y);
92 | if (this.rotated) {
93 | let xMin = this.uvRect.xMin;
94 | let yMin = this.uvRect.yMin;
95 | let yMax = this.uvRect.yMax;
96 |
97 | for (let i = 0; i < 4; i++) {
98 | let m = uv[i];
99 | let tmp = m.y;
100 | m.y = yMin + m.x - xMin;
101 | m.x = xMin + yMax - tmp;
102 | }
103 | }
104 | }
105 |
106 | public get root(): NTexture {
107 | return this._root;
108 | }
109 |
110 | public reload(nativeTexture: Texture) {
111 | if (this._root != this)
112 | throw new Error("Reload is not allow to call on none root NTexture.");
113 |
114 | if (this._nativeTexture && this._nativeTexture != nativeTexture)
115 | this._nativeTexture.dispose();
116 |
117 | this._nativeTexture = nativeTexture;
118 |
119 | if (this._nativeTexture)
120 | this.originalSize.set(nativeTexture.image.width, nativeTexture.image.height);
121 | else
122 | this.originalSize.set(0, 0);
123 | this.region.set(0, 0, this.originalSize.x, this.originalSize.y);
124 | }
125 |
126 | public dispose(): void {
127 | if (this._root == this)
128 | this._nativeTexture.dispose();
129 | }
130 | }
131 |
132 | export const EmptyTexture: NTexture = new NTexture();
--------------------------------------------------------------------------------
/src/ui/UIObjectFactory.ts:
--------------------------------------------------------------------------------
1 | import { ObjectType, PackageItemType } from "./FieldTypes";
2 | import { GButton } from "./GButton";
3 | import { GComboBox } from "./GComboBox";
4 | import { GComponent } from "./GComponent";
5 | import { GGraph } from "./GGraph";
6 | import { GGroup } from "./GGroup";
7 | import { GImage } from "./GImage";
8 | import { GLabel } from "./GLabel";
9 | import { GList } from "./GList";
10 | import { GLoader } from "./GLoader";
11 | import { GMovieClip } from "./GMovieClip";
12 | import { GObject } from "./GObject";
13 | import { GProgressBar } from "./GProgressBar";
14 | import { GRichTextField } from "./GRichTextField";
15 | import { GScrollBar } from "./GScrollBar";
16 | import { GSlider } from "./GSlider";
17 | import { GTextField } from "./GTextField";
18 | import { GTextInput } from "./GTextInput";
19 | import { GTree } from "./GTree";
20 | import { PackageItem } from "./PackageItem";
21 | import { UIPackage, Decls } from "./UIPackage";
22 | import { GLoader3D } from "./GLoader3D";
23 |
24 | export class UIObjectFactory {
25 | public static extensions: Record GComponent> = {};
26 | public static loaderType: new () => GLoader;
27 |
28 | public static setExtension(url: string, type: new () => GComponent): void {
29 | if (url == null)
30 | throw new Error("Invaild url: " + url);
31 |
32 | var pi: PackageItem = UIPackage.getItemByURL(url);
33 | if (pi)
34 | pi.extensionType = type;
35 |
36 | UIObjectFactory.extensions[url] = type;
37 | }
38 |
39 | public static setLoaderExtension(type: new () => GLoader): void {
40 | UIObjectFactory.loaderType = type;
41 | }
42 |
43 | public static resolveExtension(pi: PackageItem): void {
44 | var extensionType = UIObjectFactory.extensions["ui://" + pi.owner.id + pi.id];
45 | if (!extensionType)
46 | extensionType = UIObjectFactory.extensions["ui://" + pi.owner.name + "/" + pi.name];
47 | if (extensionType)
48 | pi.extensionType = extensionType;
49 | }
50 |
51 | public static newObject(type: number | PackageItem, userClass?: new () => GObject): GObject {
52 | var obj: GObject;
53 |
54 | if (typeof type === 'number') {
55 | switch (type) {
56 | case ObjectType.Image:
57 | return new GImage();
58 |
59 | case ObjectType.MovieClip:
60 | return new GMovieClip();
61 |
62 | case ObjectType.Component:
63 | return new GComponent();
64 |
65 | case ObjectType.Text:
66 | return new GTextField();
67 |
68 | case ObjectType.RichText:
69 | return new GRichTextField();
70 |
71 | case ObjectType.InputText:
72 | return new GTextInput();
73 |
74 | case ObjectType.Group:
75 | return new GGroup();
76 |
77 | case ObjectType.List:
78 | return new GList();
79 |
80 | case ObjectType.Graph:
81 | return new GGraph();
82 |
83 | case ObjectType.Loader:
84 | if (UIObjectFactory.loaderType)
85 | return new UIObjectFactory.loaderType();
86 | else
87 | return new GLoader();
88 |
89 | case ObjectType.Button:
90 | return new GButton();
91 |
92 | case ObjectType.Label:
93 | return new GLabel();
94 |
95 | case ObjectType.ProgressBar:
96 | return new GProgressBar();
97 |
98 | case ObjectType.Slider:
99 | return new GSlider();
100 |
101 | case ObjectType.ScrollBar:
102 | return new GScrollBar();
103 |
104 | case ObjectType.ComboBox:
105 | return new GComboBox();
106 |
107 | case ObjectType.Tree:
108 | return new GTree();
109 |
110 | case ObjectType.Loader3D:
111 | return new GLoader3D();
112 |
113 | default:
114 | return null;
115 | }
116 | }
117 | else {
118 | if (type.type == PackageItemType.Component) {
119 | if (userClass)
120 | obj = new userClass();
121 | else if (type.extensionType)
122 | obj = new type.extensionType();
123 | else
124 | obj = UIObjectFactory.newObject(type.objectType);
125 | }
126 | else
127 | obj = UIObjectFactory.newObject(type.objectType);
128 |
129 | if (obj)
130 | obj.packageItem = type;
131 | }
132 |
133 | return obj;
134 | }
135 | }
136 |
137 | Decls.UIObjectFactory = UIObjectFactory;
--------------------------------------------------------------------------------
/src/gears/GearSize.ts:
--------------------------------------------------------------------------------
1 | import { GTween } from "../tween/GTween";
2 | import { GTweener } from "../tween/GTweener";
3 | import { ByteBuffer } from "../utils/ByteBuffer";
4 | import { GearBase } from "./GearBase";
5 |
6 | export class GearSize extends GearBase {
7 | private _storage: Record;
8 | private _default: GearSizeValue;
9 |
10 | protected init(): void {
11 | this._default = {
12 | width: this._owner.width,
13 | height: this._owner.height,
14 | scaleX: this._owner.scaleX,
15 | scaleY: this._owner.scaleY
16 | };
17 | this._storage = {};
18 | }
19 |
20 | protected addStatus(pageId: string, buffer: ByteBuffer): void {
21 | var gv: GearSizeValue;
22 | if (!pageId)
23 | gv = this._default;
24 | else {
25 | gv = {};
26 | this._storage[pageId] = gv;
27 | }
28 |
29 | gv.width = buffer.readInt();
30 | gv.height = buffer.readInt();
31 | gv.scaleX = buffer.readFloat();
32 | gv.scaleY = buffer.readFloat();
33 | }
34 |
35 | public apply(): void {
36 | var gv: GearSizeValue = this._storage[this._controller.selectedPageId] || this._default;
37 |
38 | if (this.allowTween) {
39 | if (this._tweenConfig._tweener) {
40 | if (this._tweenConfig._tweener.endValue.x != gv.width || this._tweenConfig._tweener.endValue.y != gv.height
41 | || this._tweenConfig._tweener.endValue.z != gv.scaleX || this._tweenConfig._tweener.endValue.w != gv.scaleY) {
42 | this._tweenConfig._tweener.kill(true);
43 | this._tweenConfig._tweener = null;
44 | }
45 | else
46 | return;
47 | }
48 |
49 | var a: boolean = gv.width != this._owner.width || gv.height != this._owner.height;
50 | var b: boolean = gv.scaleX != this._owner.scaleX || gv.scaleY != this._owner.scaleY;
51 | if (a || b) {
52 | if (this._owner.checkGearController(0, this._controller))
53 | this._tweenConfig._displayLockToken = this._owner.addDisplayLock();
54 |
55 | this._tweenConfig._tweener = GTween.to4(this._owner.width, this._owner.height, this._owner.scaleX, this._owner.scaleY, gv.width, gv.height, gv.scaleX, gv.scaleY, this._tweenConfig.duration)
56 | .setDelay(this._tweenConfig.delay)
57 | .setEase(this._tweenConfig.easeType)
58 | .setUserData((a ? 1 : 0) + (b ? 2 : 0))
59 | .setTarget(this)
60 | .onUpdate(this.__tweenUpdate, this)
61 | .onComplete(this.__tweenComplete, this);
62 | }
63 | }
64 | else {
65 | this._owner._gearLocked = true;
66 | this._owner.setSize(gv.width, gv.height, this._owner.checkGearController(1, this._controller));
67 | this._owner.setScale(gv.scaleX, gv.scaleY);
68 | this._owner._gearLocked = false;
69 | }
70 | }
71 |
72 | private __tweenUpdate(tweener: GTweener): void {
73 | var flag: number = tweener.userData;
74 | this._owner._gearLocked = true;
75 | if ((flag & 1) != 0)
76 | this._owner.setSize(tweener.value.x, tweener.value.y, this._owner.checkGearController(1, this._controller));
77 | if ((flag & 2) != 0)
78 | this._owner.setScale(tweener.value.z, tweener.value.w);
79 | this._owner._gearLocked = false;
80 | }
81 |
82 | private __tweenComplete(): void {
83 | if (this._tweenConfig._displayLockToken != 0) {
84 | this._owner.releaseDisplayLock(this._tweenConfig._displayLockToken);
85 | this._tweenConfig._displayLockToken = 0;
86 | }
87 | this._tweenConfig._tweener = null;
88 | }
89 |
90 | public updateState(): void {
91 | var gv: GearSizeValue = this._storage[this._controller.selectedPageId];
92 | if (!gv) {
93 | gv = {};
94 | this._storage[this._controller.selectedPageId] = gv;
95 | }
96 |
97 | gv.width = this._owner.width;
98 | gv.height = this._owner.height;
99 | gv.scaleX = this._owner.scaleX;
100 | gv.scaleY = this._owner.scaleY;
101 | }
102 |
103 | public updateFromRelations(dx: number, dy: number): void {
104 | if (this._controller == null || this._storage == null)
105 | return;
106 |
107 | for (var key in this._storage) {
108 | var gv: GearSizeValue = this._storage[key];
109 | gv.width += dx;
110 | gv.height += dy;
111 | }
112 | this._default.width += dx;
113 | this._default.height += dy;
114 |
115 | this.updateState();
116 | }
117 | }
118 |
119 | interface GearSizeValue {
120 | width?: number;
121 | height?: number;
122 | scaleX?: number;
123 | scaleY?: number;
124 | }
125 |
126 |
--------------------------------------------------------------------------------
/src/ui/Relations.ts:
--------------------------------------------------------------------------------
1 | import { GComponent } from "./GComponent";
2 | import { GObject } from "./GObject";
3 | import { RelationItem } from "./RelationItem";
4 | import { ByteBuffer } from "../utils/ByteBuffer";
5 |
6 | export class Relations {
7 | private _owner: GObject;
8 | private _items: RelationItem[];
9 |
10 | public handling: GObject;
11 |
12 | constructor(owner: GObject) {
13 | this._owner = owner;
14 | this._items = [];
15 | }
16 |
17 | public add(target: GObject, relationType: number, usePercent?: boolean): void {
18 | var length: number = this._items.length;
19 | for (var i: number = 0; i < length; i++) {
20 | var item: RelationItem = this._items[i];
21 | if (item.target == target) {
22 | item.add(relationType, usePercent);
23 | return;
24 | }
25 | }
26 | var newItem: RelationItem = new RelationItem(this._owner);
27 | newItem.target = target;
28 | newItem.add(relationType, usePercent);
29 | this._items.push(newItem);
30 | }
31 |
32 | public remove(target: GObject, relationType?: number): void {
33 | relationType = relationType || 0;
34 | var cnt: number = this._items.length;
35 | var i: number = 0;
36 | while (i < cnt) {
37 | var item: RelationItem = this._items[i];
38 | if (item.target == target) {
39 | item.remove(relationType);
40 | if (item.isEmpty) {
41 | item.dispose();
42 | this._items.splice(i, 1);
43 | cnt--;
44 | }
45 | else
46 | i++;
47 | }
48 | else
49 | i++;
50 | }
51 | }
52 |
53 | public contains(target: GObject): boolean {
54 | var length: number = this._items.length;
55 | for (var i: number = 0; i < length; i++) {
56 | var item: RelationItem = this._items[i];
57 | if (item.target == target)
58 | return true;
59 | }
60 | return false;
61 | }
62 |
63 | public clearFor(target: GObject): void {
64 | var cnt: number = this._items.length;
65 | var i: number = 0;
66 | while (i < cnt) {
67 | var item: RelationItem = this._items[i];
68 | if (item.target == target) {
69 | item.dispose();
70 | this._items.splice(i, 1);
71 | cnt--;
72 | }
73 | else
74 | i++;
75 | }
76 | }
77 |
78 | public clearAll(): void {
79 | var length: number = this._items.length;
80 | for (var i: number = 0; i < length; i++) {
81 | var item: RelationItem = this._items[i];
82 | item.dispose();
83 | }
84 | this._items.length = 0;
85 | }
86 |
87 | public copyFrom(source: Relations): void {
88 | this.clearAll();
89 |
90 | var arr: RelationItem[] = source._items;
91 | var length: number = arr.length;
92 | for (var i: number = 0; i < length; i++) {
93 | var ri: RelationItem = arr[i];
94 | var item: RelationItem = new RelationItem(this._owner);
95 | item.copy(ri);
96 | this._items.push(item);
97 | }
98 | }
99 |
100 | public dispose(): void {
101 | this.clearAll();
102 | }
103 |
104 | public onOwnerSizeChanged(dWidth: number, dHeight: number, applyPivot: boolean): void {
105 | if (this._items.length == 0)
106 | return;
107 |
108 | var length: number = this._items.length;
109 | for (var i: number = 0; i < length; i++) {
110 | var item: RelationItem = this._items[i];
111 | item.applyOnSelfResized(dWidth, dHeight, applyPivot);
112 | }
113 | }
114 |
115 | public get empty(): boolean {
116 | return this._items.length == 0;
117 | }
118 |
119 | public setup(buffer: ByteBuffer, parentToChild: boolean): void {
120 | var cnt: number = buffer.readByte();
121 | var target: GObject;
122 | for (var i: number = 0; i < cnt; i++) {
123 | var targetIndex: number = buffer.readShort();
124 | if (targetIndex == -1)
125 | target = this._owner.parent;
126 | else if (parentToChild)
127 | target = ((this._owner)).getChildAt(targetIndex);
128 | else
129 | target = this._owner.parent.getChildAt(targetIndex);
130 |
131 | var newItem: RelationItem = new RelationItem(this._owner);
132 | newItem.target = target;
133 | this._items.push(newItem);
134 |
135 | var cnt2: number = buffer.readByte();
136 | for (var j: number = 0; j < cnt2; j++) {
137 | var rt: number = buffer.readByte();
138 | var usePercent: boolean = buffer.readBool();
139 | newItem.internalAdd(rt, usePercent);
140 | }
141 | }
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/src/event/EventDispatcher.ts:
--------------------------------------------------------------------------------
1 | import { Event, EventPool, EventType } from "./Event";
2 |
3 | type Listeners = { dispatching?: number, callbacks: Array, captures: Array };
4 |
5 | export class EventDispatcher {
6 | public _listeners: Record;
7 |
8 | constructor() {
9 | this._listeners = {};
10 | }
11 |
12 | public on(type: EventType, callback: Function, target?: any, capture?: boolean): void;
13 | public on(type: string, callback: Function, target?: any, capture?: boolean): void;
14 | public on(type: EventType | string, callback: Function, target?: any, capture?: boolean): void {
15 | let col = this._listeners[type];
16 | if (!col) {
17 | col = { dispatching: 0, callbacks: [], captures: [] };
18 | this._listeners[type] = col;
19 | }
20 | let arr = capture ? col.captures : col.callbacks;
21 | let index = arr.findIndex((value, index, arr) => value == callback && arr[index + 1] == target);
22 | if (index != -1)
23 | arr[index + 2] = false;
24 | else
25 | arr.push(callback, target, false);
26 | }
27 |
28 | public off(type: EventType, callback: Function, target?: any, capture?: boolean): void;
29 | public off(type: string, callback: Function, target?: any, capture?: boolean): void;
30 | public off(type: EventType | string, callback: Function, target?: any, capture?: boolean): void {
31 | let col = this._listeners[type];
32 | if (!col)
33 | return;
34 |
35 | let arr = capture ? col.captures : col.callbacks;
36 | let index = arr.findIndex((value, index, arr) => value == callback && arr[index + 1] == target);
37 | if (index != -1) {
38 | if (col.dispatching != 0) {
39 | arr[index + 2] = true;
40 | col.dispatching = 2;
41 | }
42 | else
43 | arr.splice(index, 3);
44 | }
45 | }
46 |
47 | public offAll(type?: EventType): void;
48 | public offAll(type?: string): void;
49 | public offAll(type?: EventType | string): void {
50 | if (type) {
51 | let col = this._listeners[type];
52 | if (col) {
53 | if (col.dispatching != 0) {
54 | col.callbacks.forEach((value, index, arr) => { if (index % 3 == 2) arr[index] = true; });
55 | col.captures.forEach((value, index, arr) => { if (index % 3 == 2) arr[index] = true; });
56 | col.dispatching = 2;
57 | }
58 | else {
59 | col.callbacks.length = 0;
60 | col.captures.length = 0;
61 | }
62 | }
63 | }
64 | else {
65 | for (var key in this._listeners) {
66 | delete this._listeners[key];
67 | }
68 | }
69 | }
70 |
71 | public hasListener(type: EventType, callback?: Function, target?: any, capture?: boolean): boolean;
72 | public hasListener(type: string, callback?: Function, target?: any, capture?: boolean): boolean;
73 | public hasListener(type: EventType | string, callback?: Function, target?: any, capture?: boolean): boolean {
74 | let col = this._listeners[type];
75 | if (!col)
76 | return false;
77 |
78 | let arr = capture ? col.captures : col.callbacks;
79 | if (!callback)
80 | return arr.length > 0;
81 | else
82 | arr.findIndex((value, index, arr) => value == callback && arr[index + 1] == target) != -1;
83 | }
84 |
85 | public dispatchEvent(type: EventType, data?: any): boolean;
86 | public dispatchEvent(type: string, data?: any): boolean;
87 | public dispatchEvent(type: EventType | string, data?: any): boolean {
88 | let col = this._listeners[type];
89 | if (!col || col.callbacks.length == 0 && col.captures.length == 0)
90 | return;
91 |
92 | let ev = EventPool.borrow(type);
93 | ev._type = type;
94 | ev.data = data;
95 |
96 | this._dispatch(col, ev, true);
97 | this._dispatch(col, ev, false);
98 |
99 | EventPool.returns(ev);
100 |
101 | return ev._defaultPrevented;
102 | }
103 |
104 | public _dispatch(col: Listeners, ev: Event, capture?: boolean): void {
105 | if (col.dispatching != 0)
106 | return;
107 |
108 | col.dispatching = 1;
109 | ev._sender = this;
110 | let arr = capture ? col.captures : col.callbacks;
111 |
112 | let cnt = arr.length;
113 | for (let i = 0; i < cnt; i += 3) {
114 | (arr[i]).call(arr[i + 1], ev);
115 | }
116 |
117 | if (col.dispatching == 2) {
118 | let cnt = arr.length;
119 | let i = 0;
120 | while (i < cnt) {
121 | if (arr[i + 2]) {
122 | arr.splice(i, 3);
123 | cnt -= 3;
124 | continue;
125 | }
126 | else
127 | i += 3;
128 | }
129 | }
130 | col.dispatching = 0;
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/core/Shape.ts:
--------------------------------------------------------------------------------
1 | import { DisplayObject } from "./DisplayObject";
2 | import { NGraphics } from "./NGraphics";
3 | import { RectMesh } from "./mesh/RectMesh";
4 | import { Color4 } from "../utils/Color";
5 | import { EmptyTexture } from "./NTexture";
6 | import { RoundedRectMesh } from "./mesh/RoundedRectMesh";
7 | import { EllipseMesh } from "./mesh/EllipseMesh";
8 | import { PolygonMesh } from "./mesh/PolygonMesh";
9 | import { RegularPolygonMesh } from "./mesh/RegularPolygonMesh";
10 | import { IHitTest } from "./hittest/IHitTest";
11 | import { Vector2 } from "three";
12 | import { HitTestContext } from "./Stage";
13 |
14 | export class Shape extends DisplayObject {
15 | public constructor() {
16 | super();
17 |
18 | this._graphics = new NGraphics(this._obj3D);
19 | this._graphics.texture = EmptyTexture;
20 | }
21 |
22 | public drawRect(lineWidth: number, lineColor: Color4, fillColor: Color4) {
23 | let mesh: RectMesh = this._graphics.getMeshFactory(RectMesh);
24 | mesh.lineWidth = lineWidth;
25 | mesh.lineColor = lineColor;
26 | mesh.fillColor = fillColor;
27 |
28 | this._graphics.setMeshDirty();
29 | if (fillColor.a == 1) {
30 | mesh.fillColor = null;
31 | this._graphics.color = fillColor.getHex();
32 | }
33 | else
34 | this._graphics.color = 0xFFFFFF;
35 | }
36 |
37 | public drawRoundRect(lineWidth: number, lineColor: Color4, fillColor: Color4,
38 | topLeftRadius: number, topRightRadius: number, bottomLeftRadius: number, bottomRightRadius: number) {
39 | let mesh = this._graphics.getMeshFactory(RoundedRectMesh);
40 | mesh.lineWidth = lineWidth;
41 | mesh.lineColor = lineColor;
42 | mesh.fillColor = fillColor;
43 | mesh.topLeftRadius = topLeftRadius;
44 | mesh.topRightRadius = topRightRadius;
45 | mesh.bottomLeftRadius = bottomLeftRadius;
46 | mesh.bottomRightRadius = bottomRightRadius;
47 |
48 | this._graphics.setMeshDirty();
49 | if (fillColor.a == 1) {
50 | mesh.fillColor = null;
51 | this._graphics.color = fillColor.getHex();
52 | }
53 | else
54 | this._graphics.color = 0xFFFFFF;
55 | }
56 |
57 | public drawEllipse(lineWidth: number, centerColor: Color4, lineColor: Color4, fillColor: Color4, startDegree?: number, endDegree?: number) {
58 | let mesh = this._graphics.getMeshFactory(EllipseMesh);
59 | mesh.lineWidth = lineWidth;
60 | mesh.lineColor = lineColor;
61 | mesh.fillColor = fillColor;
62 | if (centerColor.equals(fillColor))
63 | mesh.centerColor = null;
64 | else
65 | mesh.centerColor = centerColor;
66 | mesh.startDegree = startDegree;
67 | mesh.endDegreee = endDegree;
68 |
69 | this._graphics.setMeshDirty();
70 | if (fillColor.a == 1) {
71 | mesh.fillColor = null;
72 | this._graphics.color = fillColor.getHex();
73 | }
74 | else
75 | this._graphics.color = 0xFFFFFF;
76 | }
77 |
78 | public drawPolygon(points: Array, fillColor: Color4, lineWidth?: number, lineColor?: Color4) {
79 | let mesh = this._graphics.getMeshFactory(PolygonMesh);
80 | mesh.points.length = 0;
81 | mesh.points.push.apply(mesh.points, points);
82 | mesh.fillColor = fillColor;
83 | mesh.lineWidth = lineWidth;
84 | mesh.lineColor = lineColor;
85 |
86 | this._graphics.setMeshDirty();
87 | if (fillColor.a == 1) {
88 | mesh.fillColor = null;
89 | this._graphics.color = fillColor.getHex();
90 | }
91 | else
92 | this._graphics.color = 0xFFFFFF;
93 | }
94 |
95 | public drawRegularPolygon(sides: number, lineWidth: number, centerColor: Color4, lineColor: Color4,
96 | fillColor: Color4, rotation: number, distances: Array) {
97 | let mesh = this._graphics.getMeshFactory(RegularPolygonMesh);
98 | mesh.sides = sides;
99 | mesh.lineWidth = lineWidth;
100 | mesh.centerColor = centerColor;
101 | mesh.lineColor = lineColor;
102 | mesh.fillColor = fillColor;
103 | mesh.rotation = rotation;
104 | mesh.distances = distances;
105 |
106 | this._graphics.setMeshDirty();
107 | if (fillColor.a == 1) {
108 | mesh.fillColor = null;
109 | this._graphics.color = fillColor.getHex();
110 | }
111 | else
112 | this._graphics.color = 0xFFFFFF;
113 | }
114 |
115 | public clear() {
116 | this._graphics.meshFactory = null;
117 | this._graphics.setMeshDirty();
118 | }
119 |
120 | protected hitTest(context: HitTestContext): DisplayObject {
121 | if (!this._graphics.meshFactory)
122 | return null;
123 |
124 | let pt: Vector2 = context.getLocal(this);
125 |
126 | let ht: any = this._graphics.meshFactory;
127 | if ('hitTest' in ht)
128 | return (ht).hitTest(this._contentRect, pt.x, pt.y) ? this : null;
129 | else if (this._contentRect.contains(pt))
130 | return this;
131 | else
132 | return null;
133 | }
134 | }
--------------------------------------------------------------------------------
/src/utils/xml/XMLUtils.ts:
--------------------------------------------------------------------------------
1 | import { convertFromHtmlColor } from "../ToolSet";
2 |
3 | export class XMLUtils {
4 | public static decodeString(aSource: string): string {
5 | let len = aSource.length;
6 | let sb: string = "";
7 | let pos1 = 0, pos2 = 0;
8 |
9 | while (true) {
10 | pos2 = aSource.indexOf('&', pos1);
11 | if (pos2 == -1) {
12 | sb += aSource.substring(pos1);
13 | break;
14 | }
15 | sb += aSource.substring(pos1, pos2);
16 |
17 | pos1 = pos2 + 1;
18 | pos2 = pos1;
19 | let end = Math.min(len, pos2 + 10);
20 | for (; pos2 < end; pos2++) {
21 | if (aSource[pos2] == ';')
22 | break;
23 | }
24 | if (pos2 < end && pos2 > pos1) {
25 | let entity: string = aSource.substring(pos1, pos2);
26 | let u = 0;
27 | if (entity[0] == '#') {
28 | if (entity.length > 1) {
29 | if (entity[1] == 'x')
30 | u = parseInt(entity.substring(2), 16);
31 | else
32 | u = parseInt(entity.substring(1));
33 | sb += String.fromCharCode(u);
34 | pos1 = pos2 + 1;
35 | }
36 | else
37 | sb += '&';
38 | }
39 | else {
40 | switch (entity) {
41 | case "amp":
42 | u = 38;
43 | break;
44 |
45 | case "apos":
46 | u = 39;
47 | break;
48 |
49 | case "gt":
50 | u = 62;
51 | break;
52 |
53 | case "lt":
54 | u = 60;
55 | break;
56 |
57 | case "nbsp":
58 | u = 32;
59 | break;
60 |
61 | case "quot":
62 | u = 34;
63 | break;
64 | }
65 | if (u > 0) {
66 | sb += String.fromCharCode(u);
67 | pos1 = pos2 + 1;
68 | }
69 | else
70 | sb += '&';
71 | }
72 | }
73 | else {
74 | sb += '&';
75 | }
76 | }
77 |
78 | return sb;
79 | }
80 |
81 | public static encodeString(str: string): string {
82 | return str.replace(/&/g, "&").replace(//g, ">").replace(/'/g, "'").replace(/"/g, """);
84 | }
85 |
86 | public static getString(attrs: any, attrName: string, defValue?: string): string {
87 | if (attrs == null)
88 | return defValue == null ? null : defValue;
89 |
90 | let ret = attrs[attrName];
91 | if (ret != null)
92 | return "" + ret;
93 | else
94 | return defValue == null ? null : defValue;
95 | }
96 |
97 | public static getInt(attrs: any, attrName: string, defValue?: number): number {
98 | let value: string = this.getString(attrs, attrName);
99 | if (value == null || value.length == 0)
100 | return defValue == null ? 0 : defValue;
101 |
102 | if (value[value.length - 1] == '%') {
103 | let ret = parseInt(value.substring(0, value.length - 1));
104 | return Math.ceil(ret / 100.0 * defValue);
105 | }
106 | else
107 | return parseInt(value);
108 | }
109 |
110 | public static getFloat(attrs: any, attrName: string, defValue?: number): number {
111 | let value: string = this.getString(attrs, attrName);
112 | if (value == null || value.length == 0)
113 | return defValue == null ? 0 : defValue;
114 |
115 | let ret = parseFloat(value);
116 | if (isNaN(ret))
117 | return defValue == null ? 0 : defValue;
118 | else
119 | return ret;
120 | }
121 |
122 | public static getBool(attrs: any, attrName: string, defValue?: boolean): boolean {
123 | let value: string = this.getString(attrs, attrName);
124 | if (value == null || value.length == 0)
125 | return defValue == null ? false : defValue;
126 |
127 | if (value == "true" || value == "1")
128 | return true;
129 | else if (value == "false" || value == "0")
130 | return false;
131 | else
132 | return defValue == null ? false : defValue;
133 | }
134 |
135 | public static getColor(attrs: any, attrName: string, defValue?: number): number {
136 | let value: string = this.getString(attrs, attrName);
137 | if (value == null || value.length == 0)
138 | return defValue == null ? 0 : defValue;
139 |
140 | return convertFromHtmlColor(value);
141 | }
142 | }
143 |
144 | const ESCAPES: Array = [
145 | "&", "&",
146 | "<", "<",
147 | ">", ">",
148 | "'", "'",
149 | "\"", """
150 | ];
--------------------------------------------------------------------------------
/src/core/mesh/RoundedRectMesh.ts:
--------------------------------------------------------------------------------
1 | import { IMeshFactory } from "./MeshFactory";
2 | import { IHitTest } from "../hittest/IHitTest";
3 | import { Rect } from "../../utils/Rect";
4 | import { VertexBuffer } from "./VertexBuffer";
5 | import { Color4 } from "../../utils/Color";
6 |
7 | export class RoundedRectMesh implements IMeshFactory, IHitTest {
8 | public drawRect: Rect;
9 | public lineWidth: number;
10 | public lineColor: Color4;
11 | public fillColor: Color4;
12 | public topLeftRadius: number = 0;
13 | public topRightRadius: number = 0;
14 | public bottomLeftRadius: number = 0;
15 | public bottomRightRadius: number = 0;
16 |
17 | public constructor() {
18 | this.lineWidth = 1;
19 | this.lineColor = new Color4();
20 | }
21 |
22 | public onPopulateMesh(vb: VertexBuffer) {
23 | let rect: Rect = this.drawRect ? this.drawRect : vb.contentRect;
24 | let color: Color4 = this.fillColor ? this.fillColor : vb.vertexColor;
25 | let lineColor = this.lineColor;
26 |
27 | let radiusX: number = rect.width / 2;
28 | let radiusY: number = rect.height / 2;
29 | let cornerMaxRadius: number = Math.min(radiusX, radiusY);
30 | let centerX: number = radiusX + rect.x;
31 | let centerY: number = radiusY + rect.y;
32 |
33 | vb.addVert(centerX, centerY, 0, color);
34 |
35 | let cnt: number = vb.currentVertCount;
36 | for (let i: number = 0; i < 4; i++) {
37 | let radius: number = 0;
38 | switch (i) {
39 | case 0:
40 | radius = this.bottomRightRadius;
41 | break;
42 |
43 | case 1:
44 | radius = this.bottomLeftRadius;
45 | break;
46 |
47 | case 2:
48 | radius = this.topLeftRadius;
49 | break;
50 |
51 | case 3:
52 | radius = this.topRightRadius;
53 | break;
54 | }
55 | radius = Math.min(cornerMaxRadius, radius);
56 |
57 | let offsetX: number = rect.x;
58 | let offsetY: number = rect.y;
59 |
60 | if (i == 0 || i == 3)
61 | offsetX = rect.xMax - radius * 2;
62 | if (i == 0 || i == 1)
63 | offsetY = rect.yMax - radius * 2;
64 |
65 | if (radius != 0) {
66 | let partNumSides: number = Math.max(1, Math.ceil(Math.PI * radius / 8)) + 1;
67 | let angleDelta: number = Math.PI / 2 / partNumSides;
68 | let angle: number = Math.PI / 2 * i;
69 | let startAngle: number = angle;
70 |
71 | for (let j: number = 1; j <= partNumSides; j++) {
72 | if (j == partNumSides) //消除精度误差带来的不对齐
73 | angle = startAngle + Math.PI / 2;
74 | let vx = offsetX + Math.cos(angle) * (radius - this.lineWidth) + radius;
75 | let vy = offsetY + Math.sin(angle) * (radius - this.lineWidth) + radius;
76 | vb.addVert(vx, vy, 0, color);
77 | if (this.lineWidth != 0) {
78 | vb.addVert(vx, vy, 0, lineColor);
79 | vb.addVert(offsetX + Math.cos(angle) * radius + radius, offsetY + Math.sin(angle) * radius + radius, 0, lineColor);
80 | }
81 | angle += angleDelta;
82 | }
83 | }
84 | else {
85 | let vx = offsetX;
86 | let vy = offsetY;
87 | if (this.lineWidth != 0) {
88 | if (i == 0 || i == 3)
89 | offsetX -= this.lineWidth;
90 | else
91 | offsetX += this.lineWidth;
92 | if (i == 0 || i == 1)
93 | offsetY -= this.lineWidth;
94 | else
95 | offsetY += this.lineWidth;
96 | vb.addVert(offsetX, offsetY, 0, color);
97 | vb.addVert(offsetX, offsetY, 0, lineColor);
98 | vb.addVert(vx, vy, 0, lineColor);
99 | }
100 | else
101 | vb.addVert(vx, vy, 0, color);
102 | }
103 | }
104 | cnt = vb.currentVertCount - cnt;
105 |
106 | if (this.lineWidth > 0) {
107 | for (let i: number = 0; i < cnt; i += 3) {
108 | if (i != cnt - 3) {
109 | vb.addTriangle(0, i + 4, i + 1);
110 | vb.addTriangle(i + 5, i + 3, i + 2);
111 | vb.addTriangle(i + 3, i + 5, i + 6);
112 | }
113 | else {
114 | vb.addTriangle(0, 1, i + 1);
115 | vb.addTriangle(2, i + 3, i + 2);
116 | vb.addTriangle(i + 3, 2, 3);
117 | }
118 | }
119 | }
120 | else {
121 | for (let i: number = 0; i < cnt; i++)
122 | vb.addTriangle(0, (i == cnt - 1) ? 1 : i + 2, i + 1);
123 | }
124 | }
125 |
126 | public hitTest(contentRect: Rect, x: number, y: number): boolean {
127 | if (this.drawRect)
128 | return this.drawRect.contains(x, y);
129 | else
130 | return contentRect.contains(x, y);
131 | }
132 | }
--------------------------------------------------------------------------------
/src/utils/ColorMatrix.ts:
--------------------------------------------------------------------------------
1 | const LUMA_R: number = 0.299;
2 | const LUMA_G: number = 0.587;
3 | const LUMA_B: number = 0.114;
4 | const IDENTITY_MATRIX: Array = [
5 | 1, 0, 0, 0, 0,
6 | 0, 1, 0, 0, 0,
7 | 0, 0, 1, 0, 0,
8 | 0, 0, 0, 1, 0
9 | ];
10 | const LENGTH: number = IDENTITY_MATRIX.length;
11 |
12 | export class ColorMatrix {
13 | public matrix: Array;
14 |
15 | public static create(p_brightness: number, p_contrast: number, p_saturation: number, p_hue: number): ColorMatrix {
16 | var ret: ColorMatrix = new ColorMatrix();
17 | ret.adjustColor(p_brightness, p_contrast, p_saturation, p_hue);
18 | return ret;
19 | }
20 |
21 | public static getMatrix(p_brightness: number, p_contrast: number, p_saturation: number, p_hue: number, result?: number[]): number[] {
22 | if (!result)
23 | result = new Array(ColorMatrix.length);
24 | let mat = helper;
25 | mat.reset();
26 | mat.adjustColor(p_brightness, p_contrast, p_saturation, p_hue);
27 | var l: number = LENGTH;
28 | for (var i: number = 0; i < l; i++) {
29 | result[i] = mat.matrix[i];
30 | }
31 |
32 | return result;
33 | }
34 |
35 | public constructor() {
36 | this.matrix = new Array(LENGTH);
37 | this.reset();
38 | }
39 |
40 | public reset(): void {
41 | for (var i: number = 0; i < LENGTH; i++) {
42 | this.matrix[i] = IDENTITY_MATRIX[i];
43 | }
44 | }
45 |
46 | public invert(): void {
47 | this.multiplyMatrix([-1, 0, 0, 0, 255,
48 | 0, -1, 0, 0, 255,
49 | 0, 0, -1, 0, 255,
50 | 0, 0, 0, 1, 0]);
51 | }
52 |
53 | public adjustColor(p_brightness: number, p_contrast: number, p_saturation: number, p_hue: number): void {
54 | this.adjustBrightness(p_brightness);
55 | this.adjustContrast(p_contrast);
56 | this.adjustSaturation(p_saturation);
57 | this.adjustHue(p_hue);
58 | }
59 |
60 | public adjustBrightness(p_val: number): void {
61 | p_val = this.cleanValue(p_val, 1) * 255;
62 | this.multiplyMatrix([
63 | 1, 0, 0, 0, p_val,
64 | 0, 1, 0, 0, p_val,
65 | 0, 0, 1, 0, p_val,
66 | 0, 0, 0, 1, 0
67 | ]);
68 | }
69 |
70 | public adjustContrast(p_val: number): void {
71 | p_val = this.cleanValue(p_val, 1);
72 | var s: number = p_val + 1;
73 | var o: number = 128 * (1 - s);
74 | this.multiplyMatrix([
75 | s, 0, 0, 0, o,
76 | 0, s, 0, 0, o,
77 | 0, 0, s, 0, o,
78 | 0, 0, 0, 1, 0
79 | ]);
80 | }
81 |
82 | public adjustSaturation(p_val: number): void {
83 | p_val = this.cleanValue(p_val, 1);
84 | p_val += 1;
85 |
86 | var invSat: number = 1 - p_val;
87 | var invLumR: number = invSat * LUMA_R;
88 | var invLumG: number = invSat * LUMA_G;
89 | var invLumB: number = invSat * LUMA_B;
90 |
91 | this.multiplyMatrix([
92 | (invLumR + p_val), invLumG, invLumB, 0, 0,
93 | invLumR, (invLumG + p_val), invLumB, 0, 0,
94 | invLumR, invLumG, (invLumB + p_val), 0, 0,
95 | 0, 0, 0, 1, 0
96 | ]);
97 | }
98 |
99 | public adjustHue(p_val: number): void {
100 | p_val = this.cleanValue(p_val, 1);
101 | p_val *= Math.PI;
102 |
103 | var cos: number = Math.cos(p_val);
104 | var sin: number = Math.sin(p_val);
105 |
106 | this.multiplyMatrix([
107 | ((LUMA_R + (cos * (1 - LUMA_R))) + (sin * -(LUMA_R))), ((LUMA_G + (cos * -(LUMA_G))) + (sin * -(LUMA_G))), ((LUMA_B + (cos * -(LUMA_B))) + (sin * (1 - LUMA_B))), 0, 0,
108 | ((LUMA_R + (cos * -(LUMA_R))) + (sin * 0.143)), ((LUMA_G + (cos * (1 - LUMA_G))) + (sin * 0.14)), ((LUMA_B + (cos * -(LUMA_B))) + (sin * -0.283)), 0, 0,
109 | ((LUMA_R + (cos * -(LUMA_R))) + (sin * -((1 - LUMA_R)))), ((LUMA_G + (cos * -(LUMA_G))) + (sin * LUMA_G)), ((LUMA_B + (cos * (1 - LUMA_B))) + (sin * LUMA_B)), 0, 0,
110 | 0, 0, 0, 1, 0
111 | ]);
112 | }
113 |
114 | public concat(p_matrix: Array): void {
115 | if (p_matrix.length != LENGTH) { return; }
116 | this.multiplyMatrix(p_matrix);
117 | }
118 |
119 | public clone(): ColorMatrix {
120 | var result: ColorMatrix = new ColorMatrix();
121 | result.copyMatrix(this.matrix);
122 | return result;
123 | }
124 |
125 | protected copyMatrix(p_matrix: Array): void {
126 | var l: number = LENGTH;
127 | for (var i: number = 0; i < l; i++) {
128 | this.matrix[i] = p_matrix[i];
129 | }
130 | }
131 |
132 | protected multiplyMatrix(p_matrix: Array): void {
133 | var col: Array = [];
134 |
135 | var i: number = 0;
136 |
137 | for (var y: number = 0; y < 4; ++y) {
138 | for (var x: number = 0; x < 5; ++x) {
139 | col[i + x] = p_matrix[i] * this.matrix[x] +
140 | p_matrix[i + 1] * this.matrix[x + 5] +
141 | p_matrix[i + 2] * this.matrix[x + 10] +
142 | p_matrix[i + 3] * this.matrix[x + 15] +
143 | (x == 4 ? p_matrix[i + 4] : 0);
144 | }
145 |
146 | i += 5;
147 | }
148 |
149 | this.copyMatrix(col);
150 | }
151 |
152 | protected cleanValue(p_val: number, p_limit: number): number {
153 | return Math.min(p_limit, Math.max(-p_limit, p_val));
154 | }
155 | }
156 |
157 | let helper: ColorMatrix = new ColorMatrix();
--------------------------------------------------------------------------------
/src/utils/ByteBuffer.ts:
--------------------------------------------------------------------------------
1 | import { Color4 } from "./Color";
2 |
3 | export class ByteBuffer {
4 | public stringTable: Array;
5 | public version: number = 0;
6 | public littleEndian?: boolean;
7 |
8 | protected _buffer: ArrayBuffer;
9 | protected _view: DataView;
10 | protected _pos: number;
11 | protected _length: number;
12 |
13 | public constructor(buffer: ArrayBuffer, offset?: number, length?: number) {
14 | offset = offset || 0;
15 | if (length == null || length == -1)
16 | length = buffer.byteLength - offset;
17 |
18 | this._buffer = buffer;
19 | this._view = new DataView(this._buffer, offset, length);
20 | this._pos = 0;
21 | this._length = length;
22 | }
23 |
24 | public get data(): ArrayBuffer {
25 | return this._buffer;
26 | }
27 |
28 | public get pos(): number {
29 | return this._pos;
30 | }
31 |
32 | public set pos(value: number) {
33 | if (value > this._length) throw "Out of bounds";
34 | this._pos = value;
35 | }
36 |
37 | public get length(): number {
38 | return this._length;
39 | }
40 |
41 | public skip(count: number): void {
42 | this._pos += count;
43 | }
44 |
45 | private validate(forward: number): void {
46 | if (this._pos + forward > this._length) throw "Out of bounds";
47 | }
48 |
49 | public readByte(): number {
50 | this.validate(1);
51 | let ret: number = this._view.getUint8(this._pos);
52 | this._pos++;
53 | return ret;
54 | }
55 |
56 | public readBool(): boolean {
57 | return this.readByte() == 1;
58 | }
59 |
60 | public readShort(): number {
61 | this.validate(2);
62 | let ret: number = this._view.getInt16(this._pos, this.littleEndian);
63 | this._pos += 2;
64 | return ret;
65 | }
66 |
67 | public readUshort(): number {
68 | this.validate(2);
69 | let ret: number = this._view.getUint16(this._pos, this.littleEndian);
70 | this._pos += 2;
71 | return ret;
72 | }
73 |
74 | public readInt(): number {
75 | this.validate(4);
76 | let ret: number = this._view.getInt32(this._pos, this.littleEndian);
77 | this._pos += 4;
78 | return ret;
79 | }
80 |
81 | public readUint(): number {
82 | this.validate(4);
83 | let ret: number = this._view.getUint32(this._pos, this.littleEndian);
84 | this._pos += 4;
85 | return ret;
86 | }
87 |
88 | public readFloat(): number {
89 | this.validate(4);
90 | let ret: number = this._view.getFloat32(this._pos, this.littleEndian);
91 | this._pos += 4;
92 | return ret;
93 | }
94 |
95 | public readString(len?: number): string {
96 | if (len == undefined) len = this.readUshort();
97 | this.validate(len);
98 |
99 | let decoder = new TextDecoder();
100 | let ret: string = decoder.decode(new DataView(this._buffer, this._view.byteOffset + this._pos, len));
101 | this._pos += len;
102 |
103 | return ret;
104 | }
105 |
106 | public readS(): string {
107 | var index: number = this.readUshort();
108 | if (index == 65534) //null
109 | return null;
110 | else if (index == 65533)
111 | return ""
112 | else
113 | return this.stringTable[index];
114 | }
115 |
116 | public readSArray(cnt: number): Array {
117 | var ret: Array = new Array(cnt);
118 | for (var i: number = 0; i < cnt; i++)
119 | ret[i] = this.readS();
120 |
121 | return ret;
122 | }
123 |
124 | public writeS(value: string): void {
125 | var index: number = this.readUshort();
126 | if (index != 65534 && index != 65533)
127 | this.stringTable[index] = value;
128 | }
129 |
130 | public readColor(): number {
131 | var r: number = this.readByte();
132 | var g: number = this.readByte();
133 | var b: number = this.readByte();
134 | this.readByte(); //a
135 |
136 | return (r << 16) + (g << 8) + b;
137 | }
138 |
139 | public readFullColor(): Color4 {
140 | var r: number = this.readByte();
141 | var g: number = this.readByte();
142 | var b: number = this.readByte();
143 | var a: number = this.readByte();
144 | return new Color4((r << 16) + (g << 8) + b, a / 255);
145 | }
146 |
147 | public readChar(): string {
148 | var i: number = this.readUshort();
149 | return String.fromCharCode(i);
150 | }
151 |
152 | public readBuffer(): ByteBuffer {
153 | var count: number = this.readUint();
154 | this.validate(count);
155 | var ba: ByteBuffer = new ByteBuffer(this._buffer, this._view.byteOffset + this._pos, count);
156 | ba.stringTable = this.stringTable;
157 | ba.version = this.version;
158 | this._pos += count;
159 | return ba;
160 | }
161 |
162 | public seek(indexTablePos: number, blockIndex: number): boolean {
163 | var tmp: number = this._pos;
164 | this._pos = indexTablePos;
165 | var segCount: number = this.readByte();
166 | if (blockIndex < segCount) {
167 | var useShort: boolean = this.readByte() == 1;
168 | var newPos: number;
169 | if (useShort) {
170 | this._pos += 2 * blockIndex;
171 | newPos = this.readUshort();
172 | }
173 | else {
174 | this._pos += 4 * blockIndex;
175 | newPos = this.readUint();
176 | }
177 |
178 | if (newPos > 0) {
179 | this._pos = indexTablePos + newPos;
180 | return true;
181 | }
182 | else {
183 | this._pos = tmp;
184 | return false;
185 | }
186 | }
187 | else {
188 | this._pos = tmp;
189 | return false;
190 | }
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/src/ui/GScrollBar.ts:
--------------------------------------------------------------------------------
1 | import { GComponent } from "./GComponent";
2 | import { GObject } from "./GObject";
3 | import { ScrollPane } from "./ScrollPane";
4 | import { ByteBuffer } from "../utils/ByteBuffer";
5 | import { Vector2 } from "three";
6 | import { Event } from "../event/Event";
7 |
8 | var s_vec2: Vector2 = new Vector2();
9 |
10 | export class GScrollBar extends GComponent {
11 | private _grip: GObject;
12 | private _arrowButton1: GObject;
13 | private _arrowButton2: GObject;
14 | private _bar: GObject;
15 | private _target: ScrollPane;
16 |
17 | private _vertical: boolean;
18 | private _scrollPerc: number;
19 | private _fixedGripSize: boolean;
20 |
21 | private _dragOffset: Vector2;
22 | private _gripDragging: boolean;
23 |
24 | constructor() {
25 | super();
26 | this._dragOffset = new Vector2();
27 | this._scrollPerc = 0;
28 | }
29 |
30 | public setScrollPane(target: ScrollPane, vertical: boolean): void {
31 | this._target = target;
32 | this._vertical = vertical;
33 | }
34 |
35 | public setDisplayPerc(value: number) {
36 | if (this._vertical) {
37 | if (!this._fixedGripSize)
38 | this._grip.height = Math.floor(value * this._bar.height);
39 | this._grip.y = this._bar.y + (this._bar.height - this._grip.height) * this._scrollPerc;
40 |
41 | }
42 | else {
43 | if (!this._fixedGripSize)
44 | this._grip.width = Math.floor(value * this._bar.width);
45 | this._grip.x = this._bar.x + (this._bar.width - this._grip.width) * this._scrollPerc;
46 | }
47 | this._grip.visible = value != 0 && value != 1;
48 | }
49 |
50 | public setScrollPerc(val: number) {
51 | this._scrollPerc = val;
52 | if (this._vertical)
53 | this._grip.y = this._bar.y + (this._bar.height - this._grip.height) * this._scrollPerc;
54 | else
55 | this._grip.x = this._bar.x + (this._bar.width - this._grip.width) * this._scrollPerc;
56 | }
57 |
58 | public get minSize(): number {
59 | if (this._vertical)
60 | return (this._arrowButton1 ? this._arrowButton1.height : 0) + (this._arrowButton2 ? this._arrowButton2.height : 0);
61 | else
62 | return (this._arrowButton1 ? this._arrowButton1.width : 0) + (this._arrowButton2 ? this._arrowButton2.width : 0);
63 | }
64 |
65 | public get gripDragging(): boolean {
66 | return this._gripDragging;
67 | }
68 |
69 | protected constructExtension(buffer: ByteBuffer): void {
70 | buffer.seek(0, 6);
71 |
72 | this._fixedGripSize = buffer.readBool();
73 |
74 | this._grip = this.getChild("grip");
75 | if (!this._grip) {
76 | console.warn("需要定义grip");
77 | return;
78 | }
79 |
80 | this._bar = this.getChild("bar");
81 | if (!this._bar) {
82 | console.warn("需要定义bar");
83 | return;
84 | }
85 |
86 | this._arrowButton1 = this.getChild("arrow1");
87 | this._arrowButton2 = this.getChild("arrow2");
88 |
89 | this._grip.on("touch_begin", this.__gripTouchBegin, this);
90 | this._grip.on("touch_move", this.__gripTouchMove, this);
91 | this._grip.on("touch_end", this.__gripTouchEnd, this);
92 |
93 | this.on("touch_begin", this.__barTouchBegin, this);
94 |
95 | if (this._arrowButton1)
96 | this._arrowButton1.on("touch_begin", this.__arrowButton1Click, this);
97 | if (this._arrowButton2)
98 | this._arrowButton2.on("touch_begin", this.__arrowButton2Click, this);
99 | }
100 |
101 | private __gripTouchBegin(evt: Event): void {
102 | if (this._bar == null)
103 | return;
104 |
105 | evt.stopPropagation();
106 |
107 | this._gripDragging = true;
108 | this._target.updateScrollBarVisible();
109 |
110 | this.globalToLocal(evt.input.x, evt.input.y, this._dragOffset);
111 | this._dragOffset.x -= this._grip.x;
112 | this._dragOffset.y -= this._grip.y;
113 | }
114 |
115 | private __gripTouchMove(evt: Event): void {
116 | if (!this.onStage)
117 | return;
118 |
119 | var pt: Vector2 = this.globalToLocal(evt.input.x, evt.input.y, s_vec2);
120 | if (this._vertical) {
121 | let curY: number = pt.y - this._dragOffset.y;
122 | let diff = this._bar.height - this._grip.height;
123 | if (diff == 0)
124 | this._target.percY = 0;
125 | else
126 | this._target.percY = (curY - this._bar.y) / diff;
127 | }
128 | else {
129 | let curX: number = pt.x - this._dragOffset.x;
130 | let diff = this._bar.width - this._grip.width;
131 | if (diff == 0)
132 | this._target.percX = 0;
133 | else
134 | this._target.percX = (curX - this._bar.x) / (this._bar.width - this._grip.width);
135 | }
136 | }
137 |
138 | private __gripTouchEnd(evt: Event): void {
139 | this._gripDragging = false;
140 | this._target.updateScrollBarVisible();
141 | }
142 |
143 | private __arrowButton1Click(evt: Event): void {
144 | evt.stopPropagation();
145 |
146 | if (this._vertical)
147 | this._target.scrollUp();
148 | else
149 | this._target.scrollLeft();
150 | }
151 |
152 | private __arrowButton2Click(evt: Event): void {
153 | evt.stopPropagation();
154 |
155 | if (this._vertical)
156 | this._target.scrollDown();
157 | else
158 | this._target.scrollRight();
159 | }
160 |
161 | private __barTouchBegin(evt: Event): void {
162 | evt.stopPropagation();
163 |
164 | var pt: Vector2 = this._grip.globalToLocal(evt.input.x, evt.input.y, s_vec2);
165 | if (this._vertical) {
166 | if (pt.y < 0)
167 | this._target.scrollUp(4);
168 | else
169 | this._target.scrollDown(4);
170 | }
171 | else {
172 | if (pt.x < 0)
173 | this._target.scrollLeft(4);
174 | else
175 | this._target.scrollRight(4);
176 | }
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/src/core/NMaterial.ts:
--------------------------------------------------------------------------------
1 | import { ShaderMaterial, ShaderLib, UniformsUtils, Uniform, Matrix4, Vector4, Texture, DoubleSide, DepthModes, Color } from "three";
2 |
3 | export class NMaterial extends ShaderMaterial {
4 | public map: Texture;
5 |
6 | public constructor() {
7 | super();
8 |
9 | let customUniforms = UniformsUtils.merge([
10 | ShaderLib.basic.uniforms,
11 | { _ColorMatrix: new Uniform(new Matrix4()) },
12 | { _ColorOffset: new Uniform(new Vector4()) },
13 | {
14 | diffuse: {
15 | value: new Color(0xffffff)
16 | }
17 | }
18 | ]);
19 |
20 | this.uniforms = customUniforms;
21 | this.vertexShader = `
22 | #ifdef TEXT
23 | #define USE_UV
24 | #endif
25 | #include
26 | #include
27 | #include
28 | varying vec4 vColor;
29 | attribute vec4 color;
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 |
36 | void main() {
37 |
38 | #include
39 |
40 | vColor = color;
41 |
42 | #include
43 |
44 | #ifdef USE_ENVMAP
45 |
46 | #include
47 | #include
48 | #include
49 | #include
50 |
51 | #endif
52 |
53 | #include
54 | #include
55 | #include
56 | #include
57 | #include
58 |
59 | #include
60 | #include
61 | #include
62 | #include
63 |
64 | }
65 | `;
66 |
67 | this.fragmentShader = `
68 | #ifdef TEXT
69 | #define USE_UV
70 | #endif
71 | uniform bool grayed;
72 | uniform bool colorFilter;
73 | uniform mat4 colorMatrix;
74 | uniform vec4 colorOffset;
75 |
76 | uniform vec3 diffuse;
77 | uniform float opacity;
78 | #ifndef FLAT_SHADED
79 | varying vec3 vNormal;
80 | #endif
81 | #include
82 | #include
83 |
84 | varying vec4 vColor;
85 |
86 | #include
87 | #include
88 | #include
89 | #include
90 | #include
91 | #include
92 | #include
93 | #include
94 | #include
95 | #include
96 | #include
97 | #include
98 | void main() {
99 | #include
100 | vec4 diffuseColor = vec4( diffuse, opacity );
101 | #include
102 | #ifdef USE_MAP
103 | #ifdef TEXT
104 | vec4 sampleColor = texture2D( map, vUv );
105 | if(vColor.a<0.1)
106 | diffuseColor.a *= sampleColor.r;
107 | else if(vColor.a<0.4)
108 | diffuseColor.a *= sampleColor.g;
109 | else
110 | diffuseColor.a *= sampleColor.b;
111 | #else
112 | #include
113 | #endif
114 | #endif
115 |
116 | #ifdef TEXT
117 | diffuseColor.rgb *= vColor.rgb;
118 | #else
119 | diffuseColor *= vColor;
120 | #endif
121 |
122 | #include
123 | #include
124 | #include
125 | ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
126 | // accumulation (baked indirect lighting only)
127 | #ifdef USE_LIGHTMAP
128 | vec4 lightMapTexel= texture2D( lightMap, vUv2 );
129 | reflectedLight.indirectDiffuse += lightMapTexelToLinear( lightMapTexel ).rgb * lightMapIntensity;
130 | #else
131 | reflectedLight.indirectDiffuse += vec3( 1.0 );
132 | #endif
133 | // modulation
134 | #include
135 | reflectedLight.indirectDiffuse *= diffuseColor.rgb;
136 | vec3 outgoingLight = reflectedLight.indirectDiffuse;
137 | #include
138 | gl_FragColor = vec4( outgoingLight, diffuseColor.a );
139 | #include
140 | #include
141 | #include
142 | #include
143 | #include
144 |
145 | #ifdef GRAYED
146 | float grey = dot(gl_FragColor.rgb, vec3(0.299, 0.587, 0.114));
147 | gl_FragColor.rgb = vec3(grey, grey, grey);
148 | #endif
149 |
150 | #ifdef COLOR_FILTER
151 | vec4 col = gl_FragColor;
152 | gl_FragColor.r = dot(col, _ColorMatrix[0]) + _ColorOffset.x;
153 | gl_FragColor.g = dot(col, _ColorMatrix[1]) + _ColorOffset.y;
154 | gl_FragColor.b = dot(col, _ColorMatrix[2]) + _ColorOffset.z;
155 | gl_FragColor.a = dot(col, _ColorMatrix[3]) + _ColorOffset.w;
156 | #endif
157 | }
158 | `;
159 |
160 | this.name = "ui-material";
161 | this.lights = false;
162 | this.transparent = true;
163 | this.depthTest = false;
164 | this.side = DoubleSide;
165 | //this.wireframe = true;
166 | (this)["isMeshBasicMaterial"] = true;
167 | }
168 | }
--------------------------------------------------------------------------------
/src/utils/UBBParser.ts:
--------------------------------------------------------------------------------
1 |
2 | export class UBBParser {
3 | private _text: string;
4 | private _readPos: number = 0;
5 |
6 | protected _handlers: Record string>;
7 |
8 | public defaultImgWidth: number = 0;
9 | public defaultImgHeight: number = 0;
10 | public lastColor: string;
11 | public lastSize: string;
12 |
13 | constructor() {
14 | this._handlers = {};
15 | this._handlers["url"] = this.onTag_URL;
16 | this._handlers["img"] = this.onTag_IMG;
17 | this._handlers["b"] = this.onTag_B;
18 | this._handlers["i"] = this.onTag_I;
19 | this._handlers["u"] = this.onTag_U;
20 | this._handlers["sup"] = this.onTag_Simple;
21 | this._handlers["sub"] = this.onTag_Simple;
22 | this._handlers["color"] = this.onTag_COLOR;
23 | this._handlers["font"] = this.onTag_FONT;
24 | this._handlers["size"] = this.onTag_SIZE;
25 | }
26 |
27 | protected onTag_URL(tagName: string, end: boolean, attr: string): string {
28 | if (!end) {
29 | if (attr != null)
30 | return "";
31 | else {
32 | var href: string = this.getTagText();
33 | return "";
34 | }
35 | }
36 | else
37 | return "";
38 | }
39 |
40 | protected onTag_IMG(tagName: string, end: boolean, attr: string): string {
41 | if (!end) {
42 | var src: string = this.getTagText(true);
43 | if (!src)
44 | return null;
45 |
46 | if (this.defaultImgWidth)
47 | return "
";
48 | else
49 | return "
";
50 | }
51 | else
52 | return null;
53 | }
54 |
55 | protected onTag_B(tagName: string, end: boolean, attr: string): string {
56 | return end ? ("") : ("");
57 | }
58 |
59 | protected onTag_I(tagName: string, end: boolean, attr: string): string {
60 | return end ? ("") : ("");
61 | }
62 |
63 | protected onTag_U(tagName: string, end: boolean, attr: string): string {
64 | return end ? ("") : ("");
65 | }
66 |
67 | protected onTag_Simple(tagName: string, end: boolean, attr: string): string {
68 | return end ? ("" + tagName + ">") : ("<" + tagName + ">");
69 | }
70 |
71 | protected onTag_COLOR(tagName: string, end: boolean, attr: string): string {
72 | if (!end) {
73 | this.lastColor = attr;
74 | return "";
75 | }
76 | else
77 | return "";
78 | }
79 |
80 | protected onTag_FONT(tagName: string, end: boolean, attr: string): string {
81 | if (!end)
82 | return "";
83 | else
84 | return "";
85 | }
86 |
87 | protected onTag_SIZE(tagName: string, end: boolean, attr: string): string {
88 | if (!end) {
89 | this.lastSize = attr;
90 | return "";
91 | }
92 | else
93 | return "";
94 | }
95 |
96 | protected getTagText(remove?: boolean): string {
97 | var pos1: number = this._readPos;
98 | var pos2: number;
99 | var result: string = "";
100 | while ((pos2 = this._text.indexOf("[", pos1)) != -1) {
101 | if (this._text.charCodeAt(pos2 - 1) == 92)//\
102 | {
103 | result += this._text.substring(pos1, pos2 - 1);
104 | result += "[";
105 | pos1 = pos2 + 1;
106 | }
107 | else {
108 | result += this._text.substring(pos1, pos2);
109 | break;
110 | }
111 | }
112 | if (pos2 == -1)
113 | return null;
114 |
115 | if (remove)
116 | this._readPos = pos2;
117 |
118 | return result;
119 | }
120 |
121 | public parse(text: string, remove?: boolean): string {
122 | this._text = text;
123 | this.lastColor = null;
124 | this.lastSize = null;
125 |
126 | var pos1: number = 0, pos2: number, pos3: number;
127 | var end: boolean;
128 | var tag: string, attr: string;
129 | var repl: string;
130 | var func: Function;
131 | var result: string = "";
132 | while ((pos2 = this._text.indexOf("[", pos1)) != -1) {
133 | if (pos2 > 0 && this._text.charCodeAt(pos2 - 1) == 92)//\
134 | {
135 | result += this._text.substring(pos1, pos2 - 1);
136 | result += "[";
137 | pos1 = pos2 + 1;
138 | continue;
139 | }
140 |
141 | result += this._text.substring(pos1, pos2);
142 | pos1 = pos2;
143 | pos2 = this._text.indexOf("]", pos1);
144 | if (pos2 == -1)
145 | break;
146 |
147 | end = this._text.charAt(pos1 + 1) == '/';
148 | tag = this._text.substring(end ? pos1 + 2 : pos1 + 1, pos2);
149 | this._readPos = pos2 + 1;
150 | attr = null;
151 | repl = null;
152 | pos3 = tag.indexOf("=");
153 | if (pos3 != -1) {
154 | attr = tag.substring(pos3 + 1);
155 | tag = tag.substring(0, pos3);
156 | }
157 | tag = tag.toLowerCase();
158 | func = this._handlers[tag];
159 | if (func != null) {
160 | if (!remove) {
161 | repl = func.call(this, tag, end, attr);
162 | if (repl != null)
163 | result += repl;
164 | }
165 | }
166 | else
167 | result += this._text.substring(pos1, this._readPos);
168 | pos1 = this._readPos;
169 | }
170 |
171 | if (pos1 < this._text.length)
172 | result += this._text.substring(pos1);
173 |
174 | this._text = null;
175 |
176 | return result;
177 | }
178 | }
179 |
180 | export var defaultParser: UBBParser = new UBBParser();
--------------------------------------------------------------------------------