├── README ├── assets ├── assets.go ├── assets_test.go ├── lru.go ├── template.go └── world.json ├── data.txt ├── main.go ├── static ├── css │ └── app.css ├── images │ ├── dirt.jpg │ └── floor.jpg └── js │ ├── ColladaLoader.js │ ├── FlyControls.js │ ├── MTLLoader.js │ ├── OBJLoader.js │ ├── OBJMTLLoader.js │ ├── PointerLockControls.js │ ├── SceneLoader.js │ ├── app.js │ ├── jquery-2.1.1.min.js │ ├── three.min.js │ └── ws.js ├── store.go ├── templates ├── index.html └── test.html ├── universe.go ├── universe.toml ├── util ├── config.go ├── example.toml └── toml_test.go ├── world ├── generate │ ├── basic.go │ ├── caves.go │ ├── cli │ │ ├── cli │ │ └── main.go │ ├── export.go │ ├── gen_test.go │ └── generate.go ├── messaging.go ├── player.go ├── prop.go ├── sector.go ├── stuff_test.go ├── tileset.go ├── world.go └── world_loader.go └── wshub.go /README: -------------------------------------------------------------------------------- 1 | # Cohort 2 | 3 | A golang application to preset a threejs interface and get all it's assets out of IPFS ( http://ipfs.io ) 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/assets.go: -------------------------------------------------------------------------------- 1 | package assets 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "io/ioutil" 8 | "sync" 9 | 10 | "net/http" 11 | "net/url" 12 | ) 13 | 14 | const ( 15 | api = "/api/v0/" 16 | ipfsHost = "localhost:5001" 17 | Max = 600 // investigate byte limit 18 | ) 19 | 20 | type Caches interface { 21 | Get(s string) []byte 22 | } 23 | 24 | type dataBlock []byte 25 | 26 | //type Ref map[string]string 27 | type Ref struct { 28 | Key string 29 | Message string 30 | } 31 | 32 | type Cache struct { 33 | name string 34 | origin string 35 | lock sync.Mutex 36 | nameCache map[string]string 37 | nameLock sync.Mutex 38 | lru *Lru 39 | } 40 | 41 | func NewCache() *Cache { 42 | c := &Cache{} 43 | c.nameCache = make(map[string]string) 44 | c.lru = NewLru(Max) 45 | return c 46 | } 47 | 48 | type DummyCache struct { 49 | path string 50 | local map[string][]byte 51 | lock sync.Mutex 52 | } 53 | 54 | func (c *Cache) Req(path string, arg string) (resp *http.Response, err error) { 55 | u := url.URL{} 56 | u.Scheme = "http" 57 | u.Host = ipfsHost 58 | u.Path = api + path 59 | if arg != "" { 60 | val := url.Values{} 61 | val.Set("arg", arg) 62 | val.Set("encoding", "json") 63 | u.RawQuery = val.Encode() 64 | } 65 | //TODO need to parse and return http status 66 | fmt.Println(u.String()) 67 | resp, err = http.Get(u.String()) 68 | if resp.StatusCode != 200 { 69 | return resp, errors.New(resp.Status) 70 | } 71 | if err != nil { 72 | return resp, err 73 | } 74 | return resp, err 75 | } 76 | 77 | // like this /api/v0/name/resolve?arg=QmZXxbfUdRYi578pectWLFNFv5USQrsXdYAGeCsMJ6X8Zt&encoding=json 78 | func (c *Cache) Resolve(name string) (ref string, err error) { 79 | val, ok := c.nameCache[name] 80 | if ok { 81 | fmt.Println("in name cache") 82 | return val, err 83 | } 84 | data, err := c.Get("name/resolve", name) 85 | if err != nil { 86 | fmt.Println("resolve error ", err) 87 | return "", err 88 | } 89 | refObj := &Ref{} 90 | fmt.Println("start unmarshall") 91 | merr := json.Unmarshal(data, &refObj) 92 | fmt.Println(refObj) 93 | if merr != nil { 94 | fmt.Println("unmarshall error ", merr) 95 | return "", err 96 | } 97 | if refObj.Key == "" { 98 | fmt.Println("key error ", merr) 99 | return "", err 100 | } 101 | ref = refObj.Key 102 | c.nameLock.Lock() 103 | fmt.Println("add name ", name, " to cache") 104 | c.nameCache[name] = ref 105 | c.nameLock.Unlock() 106 | return ref, err 107 | } 108 | 109 | func (c *Cache) Ls(name string) (data []byte, err error) { 110 | data, err = c.Get("ls", name) 111 | return data, err 112 | } 113 | 114 | func (c *Cache) Diag() (data []byte, err error) { 115 | diag, err := c.Get("/diag/net", "") 116 | return diag, err 117 | } 118 | 119 | func (c *Cache) Cat(s string) (data dataBlock, err error) { 120 | val, ok := c.lru.Get(s) 121 | if !ok { 122 | // not in cache 123 | data, err = c.Get("cat", s) 124 | if err != nil { 125 | fmt.Println("cat error ", err, data) 126 | return data, err 127 | } 128 | fmt.Println("add to cache", s) 129 | c.lru.Add(s, data) 130 | return data, nil 131 | } 132 | //fmt.Println("in cache ", s) 133 | return val, nil 134 | } 135 | 136 | // get an object from the cache 137 | func (c *Cache) Get(s string, a string) (data dataBlock, err error) { 138 | fmt.Println(s) 139 | resp, err := c.Req(s, a) 140 | if err != nil { 141 | fmt.Println(err) 142 | return dataBlock{}, err 143 | } 144 | fmt.Println(resp.Status) 145 | data, err = ioutil.ReadAll(resp.Body) 146 | resp.Body.Close() 147 | if err != nil { 148 | fmt.Println(err) 149 | return dataBlock{}, err 150 | } 151 | //fmt.Printf("%s", data) 152 | return data, err 153 | } 154 | 155 | func (c *Cache) Listing(path string) (items []string, err error) { 156 | resp, err := c.Ls(path) 157 | li := &Listing{} 158 | if err != nil { 159 | return items, err 160 | } 161 | fmt.Println("start unmarshall") 162 | merr := json.Unmarshal(resp, &li) 163 | if merr != nil { 164 | fmt.Println("Unmarshall error ", err) 165 | return items, merr 166 | } 167 | for _, it := range li.Objects[0].Links { 168 | //fmt.Println("listing ", i, it) 169 | items = append(items, it.Name) 170 | } 171 | return items, nil 172 | } 173 | 174 | type Item struct { 175 | Name string 176 | Hash string 177 | Size int64 178 | } 179 | 180 | type List struct { 181 | Hash string 182 | Links []Item 183 | } 184 | 185 | type Listing struct { 186 | Objects []List 187 | } 188 | -------------------------------------------------------------------------------- /assets/assets_test.go: -------------------------------------------------------------------------------- 1 | package assets 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | //"io/ioutil" 7 | //"net/http" 8 | "fmt" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/zignig/cohort/util" 13 | ) 14 | 15 | func TestTiles(t *testing.T) { 16 | conf := util.GetConfig("../universe.toml") 17 | tiles := conf.Tile 18 | c := NewCache() 19 | st, err := c.Ls(tiles) 20 | if err != nil { 21 | fmt.Println("FAIL resolve") 22 | } 23 | fmt.Print(string(st)) 24 | tileList := &Listing{} 25 | json.Unmarshal(st, tileList) 26 | links := tileList.Objects[0].Links 27 | 28 | for i := range links { 29 | TileName := links[i].Name 30 | if strings.HasSuffix(TileName, "obj") { 31 | fmt.Println(TileName) 32 | } 33 | } 34 | fmt.Println(len(links), " tiles in hash") 35 | 36 | } 37 | 38 | func aTestCache(t *testing.T) { 39 | conf := util.GetConfig("../universe.toml") 40 | baseRef := conf.Ref 41 | fmt.Println(conf) 42 | c := NewCache() 43 | fmt.Println(c) 44 | //c.Diag() 45 | st, err := c.Resolve(baseRef) 46 | if err != nil { 47 | fmt.Println("FAIL resolve") 48 | } 49 | fmt.Println(st) 50 | data, err := c.Cat(st + "/" + conf.Path) 51 | if err != nil { 52 | fmt.Println("ls errror ", err) 53 | } 54 | fmt.Println(string(data)) 55 | //fmt.Println(string(data)) 56 | // TODO need to decode json stuff in assets ( dodj hack ) 57 | //d, e := c.Ls(string(st[1 : len(st)-1])) 58 | //fmt.Println(string(d), e) 59 | 60 | // import export struct tests 61 | //export() 62 | 63 | d, err := c.Ls(st) 64 | if err != nil { 65 | fmt.Println("ls errror ", err) 66 | } 67 | fmt.Println(string(d)) 68 | // broken hash 69 | d, err = c.Ls("Qmeq1j9dwd3xY4e6D6Qtrvvbr6DXF3diKLvbS2ApBb1T6j") 70 | if err != nil { 71 | fmt.Println("ls errror ", err) 72 | } 73 | fmt.Println(string(d)) 74 | 75 | //c.Cat("QmTJK6iE6hhBXCYAReV9ftQVnY8eTkyWcQMF5cQiSyD2ty") 76 | 77 | //c.Ls("QmVyRrPEvAtTEDLKyEZWVMUwN9w3iJJxkN4uiCNNWoSyUQ") 78 | 79 | //c.Cat("QmTJK6iE6hhBXCYAReV9ftQVnY8eTkyWcQMF5cQiSyD2ty") 80 | export() 81 | //p := V3{} 82 | 83 | //dump(p) 84 | } 85 | -------------------------------------------------------------------------------- /assets/lru.go: -------------------------------------------------------------------------------- 1 | /* 2 | Modified 3 | 4 | Copyright 2013 Google Inc. 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | */ 18 | 19 | // Package lru implements an LRU cache. 20 | package assets 21 | 22 | import ( 23 | "container/list" 24 | "sync" 25 | ) 26 | 27 | // Lru is an LRU cache. It is not safe for concurrent access. 28 | type Lru struct { 29 | // MaxEntries is the maximum number of cache entries before 30 | // an item is evicted. Zero means no limit. 31 | MaxEntries int 32 | MaxSize int64 33 | lock sync.Mutex 34 | 35 | // OnEvicted optionally specificies a callback function to be 36 | // executed when an entry is purged from the cache. 37 | OnEvicted func(key Key, value interface{}) 38 | 39 | ll *list.List 40 | cache map[interface{}]*list.Element 41 | } 42 | 43 | // A Key may be any value that is comparable. See http://golang.org/ref/spec#Comparison_operators 44 | type Key interface{} 45 | 46 | type entry struct { 47 | key Key 48 | value dataBlock 49 | } 50 | 51 | // New creates a new Lru. 52 | // If maxEntries is zero, the cache has no limit and it's assumed 53 | // that eviction is done by the caller. 54 | func NewLru(maxEntries int) *Lru { 55 | return &Lru{ 56 | MaxEntries: maxEntries, 57 | ll: list.New(), 58 | cache: make(map[interface{}]*list.Element), 59 | } 60 | } 61 | 62 | // Add adds a value to the cache. 63 | func (c *Lru) Add(key Key, value dataBlock) { 64 | c.lock.Lock() 65 | defer c.lock.Unlock() 66 | if c.cache == nil { 67 | c.cache = make(map[interface{}]*list.Element) 68 | c.ll = list.New() 69 | } 70 | if ee, ok := c.cache[key]; ok { 71 | c.ll.MoveToFront(ee) 72 | value = ee.Value.(dataBlock) 73 | return 74 | } 75 | ele := c.ll.PushFront(&entry{key, value}) 76 | c.cache[key] = ele 77 | if c.MaxEntries != 0 && c.ll.Len() > c.MaxEntries { 78 | c.RemoveOldest() 79 | } 80 | } 81 | 82 | // Get looks up a key's value from the cache. 83 | func (c *Lru) Get(key Key) (value dataBlock, ok bool) { 84 | if c.cache == nil { 85 | return 86 | } 87 | if ele, hit := c.cache[key]; hit { 88 | c.ll.MoveToFront(ele) 89 | return ele.Value.(*entry).value, true 90 | } 91 | return 92 | } 93 | 94 | // Remove removes the provided key from the cache. 95 | func (c *Lru) Remove(key Key) { 96 | if c.cache == nil { 97 | return 98 | } 99 | if ele, hit := c.cache[key]; hit { 100 | c.removeElement(ele) 101 | } 102 | } 103 | 104 | // RemoveOldest removes the oldest item from the cache. 105 | func (c *Lru) RemoveOldest() { 106 | if c.cache == nil { 107 | return 108 | } 109 | ele := c.ll.Back() 110 | if ele != nil { 111 | c.removeElement(ele) 112 | } 113 | } 114 | 115 | func (c *Lru) removeElement(e *list.Element) { 116 | c.ll.Remove(e) 117 | kv := e.Value.(*entry) 118 | delete(c.cache, kv.key) 119 | if c.OnEvicted != nil { 120 | c.OnEvicted(kv.key, kv.value) 121 | } 122 | } 123 | 124 | // Len returns the number of items in the cache. 125 | func (c *Lru) Len() int { 126 | if c.cache == nil { 127 | return 0 128 | } 129 | return c.ll.Len() 130 | } 131 | -------------------------------------------------------------------------------- /assets/template.go: -------------------------------------------------------------------------------- 1 | package assets 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | const Sectors = 8 9 | 10 | type V3 struct { 11 | X float64 12 | Y float64 13 | Z float64 14 | } 15 | 16 | // 4 d euler 17 | type E4 struct { 18 | X float64 19 | Y float64 20 | Z float64 21 | W float64 22 | } 23 | 24 | type Reference struct { 25 | Ips string 26 | IsName bool `json:",omitempty"` 27 | Path string 28 | } 29 | 30 | type WorldStore struct { 31 | Title string 32 | Grid [][]Reference 33 | } 34 | 35 | func (c *Cache) LoadWorldStore(data []byte) (ws *WorldStore, err error) { 36 | err = json.Unmarshal(data, &ws) 37 | return ws, err 38 | } 39 | 40 | func (c *Cache) LoadSectorStore(data []byte) (ss *SectorStore, err error) { 41 | err = json.Unmarshal(data, &ss) 42 | return ss, err 43 | } 44 | 45 | func NewWorldStore() *WorldStore { 46 | ws := &WorldStore{} 47 | grid := make([][]Reference, Sectors) 48 | for i := range grid { 49 | grid[i] = make([]Reference, Sectors) 50 | } 51 | ws.Grid = grid 52 | return ws 53 | } 54 | 55 | type SectorStore struct { 56 | Ref string 57 | Assets []*AssetItem 58 | } 59 | 60 | type AssetItem struct { 61 | Path string `json:"path"` 62 | Pos V3 63 | Rot E4 64 | } 65 | 66 | func export() { 67 | ws := &SectorStore{} 68 | ws.Assets = append(ws.Assets, &AssetItem{}) 69 | j, err := json.MarshalIndent(ws, "", "\t") 70 | if err != nil { 71 | fmt.Println("json error", err.Error()) 72 | } 73 | fmt.Println(string(j)) 74 | } 75 | 76 | func dump(ws interface{}) { 77 | j, err := json.MarshalIndent(ws, "", "\t") 78 | if err != nil { 79 | fmt.Println("json error", err.Error()) 80 | } 81 | fmt.Println(string(j)) 82 | } 83 | -------------------------------------------------------------------------------- /assets/world.json: -------------------------------------------------------------------------------- 1 | { 2 | "Title": "Test World o stuff", 3 | "Grid": [ 4 | [ 5 | { 6 | "Ips": "", 7 | "Path": "" 8 | }, 9 | { 10 | "Ips": "", 11 | "Path": "" 12 | }, 13 | { 14 | "Ips": "", 15 | "Path": "" 16 | }, 17 | { 18 | "Ips": "", 19 | "Path": "" 20 | }, 21 | { 22 | "Ips": "", 23 | "Path": "" 24 | }, 25 | { 26 | "Ips": "", 27 | "Path": "" 28 | }, 29 | { 30 | "Ips": "", 31 | "Path": "" 32 | }, 33 | { 34 | "Ips": "", 35 | "Path": "" 36 | } 37 | ], 38 | [ 39 | { 40 | "Ips": "", 41 | "Path": "" 42 | }, 43 | { 44 | "Ips": "", 45 | "Path": "" 46 | }, 47 | { 48 | "Ips": "", 49 | "Path": "" 50 | }, 51 | { 52 | "Ips": "", 53 | "Path": "" 54 | }, 55 | { 56 | "Ips": "", 57 | "Path": "" 58 | }, 59 | { 60 | "Ips": "", 61 | "Path": "" 62 | }, 63 | { 64 | "Ips": "", 65 | "Path": "" 66 | }, 67 | { 68 | "Ips": "", 69 | "Path": "" 70 | } 71 | ], 72 | [ 73 | { 74 | "Ips": "", 75 | "Path": "" 76 | }, 77 | { 78 | "Ips": "", 79 | "Path": "" 80 | }, 81 | { 82 | "Ips": "", 83 | "Path": "" 84 | }, 85 | { 86 | "Ips": "", 87 | "Path": "" 88 | }, 89 | { 90 | "Ips": "", 91 | "Path": "" 92 | }, 93 | { 94 | "Ips": "", 95 | "Path": "" 96 | }, 97 | { 98 | "Ips": "", 99 | "Path": "" 100 | }, 101 | { 102 | "Ips": "", 103 | "Path": "" 104 | } 105 | ], 106 | [ 107 | { 108 | "Ips": "", 109 | "Path": "" 110 | }, 111 | { 112 | "Ips": "", 113 | "Path": "" 114 | }, 115 | { 116 | "Ips": "", 117 | "Path": "" 118 | }, 119 | { 120 | "Ips": "", 121 | "Path": "" 122 | }, 123 | { 124 | "Ips": "", 125 | "Path": "" 126 | }, 127 | { 128 | "Ips": "", 129 | "Path": "" 130 | }, 131 | { 132 | "Ips": "", 133 | "Path": "" 134 | }, 135 | { 136 | "Ips": "", 137 | "Path": "" 138 | } 139 | ], 140 | [ 141 | { 142 | "Ips": "", 143 | "Path": "" 144 | }, 145 | { 146 | "Ips": "", 147 | "Path": "" 148 | }, 149 | { 150 | "Ips": "", 151 | "Path": "" 152 | }, 153 | { 154 | "Ips": "", 155 | "Path": "" 156 | }, 157 | { 158 | "Ips": "", 159 | "Path": "" 160 | }, 161 | { 162 | "Ips": "", 163 | "Path": "" 164 | }, 165 | { 166 | "Ips": "", 167 | "Path": "" 168 | }, 169 | { 170 | "Ips": "", 171 | "Path": "" 172 | } 173 | ], 174 | [ 175 | { 176 | "Ips": "", 177 | "Path": "" 178 | }, 179 | { 180 | "Ips": "", 181 | "Path": "" 182 | }, 183 | { 184 | "Ips": "", 185 | "Path": "" 186 | }, 187 | { 188 | "Ips": "", 189 | "Path": "" 190 | }, 191 | { 192 | "Ips": "", 193 | "Path": "" 194 | }, 195 | { 196 | "Ips": "", 197 | "Path": "" 198 | }, 199 | { 200 | "Ips": "", 201 | "Path": "" 202 | }, 203 | { 204 | "Ips": "", 205 | "Path": "" 206 | } 207 | ], 208 | [ 209 | { 210 | "Ips": "", 211 | "Path": "" 212 | }, 213 | { 214 | "Ips": "", 215 | "Path": "" 216 | }, 217 | { 218 | "Ips": "", 219 | "Path": "" 220 | }, 221 | { 222 | "Ips": "", 223 | "Path": "" 224 | }, 225 | { 226 | "Ips": "", 227 | "Path": "" 228 | }, 229 | { 230 | "Ips": "", 231 | "Path": "" 232 | }, 233 | { 234 | "Ips": "", 235 | "Path": "" 236 | }, 237 | { 238 | "Ips": "", 239 | "Path": "" 240 | } 241 | ], 242 | [ 243 | { 244 | "Ips": "", 245 | "Path": "" 246 | }, 247 | { 248 | "Ips": "", 249 | "Path": "" 250 | }, 251 | { 252 | "Ips": "", 253 | "Path": "" 254 | }, 255 | { 256 | "Ips": "", 257 | "Path": "" 258 | }, 259 | { 260 | "Ips": "", 261 | "Path": "" 262 | }, 263 | { 264 | "Ips": "", 265 | "Path": "" 266 | }, 267 | { 268 | "Ips": "", 269 | "Path": "" 270 | }, 271 | { 272 | "Ips": "", 273 | "Path": "" 274 | } 275 | ] 276 | ] 277 | } 278 | -------------------------------------------------------------------------------- /data.txt: -------------------------------------------------------------------------------- 1 | QmZXxbfUdRYi578pectWLFNFv5USQrsXdYAGeCsMJ6X8Zt 2 | QmRwGRi6cpaP5DrCRRR1pHXxqam63mjHiyDRUo8LxqY8yg ipfslogo.txt 3 | 4 | tile source http://www.racoon-media.nl/ -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "html/template" 6 | "net/http" 7 | "os" 8 | 9 | "github.com/GeertJohan/go.rice" 10 | "github.com/gin-gonic/gin" 11 | "github.com/gorilla/websocket" 12 | "github.com/op/go-logging" 13 | "github.com/zignig/cohort/util" 14 | ) 15 | 16 | var u *universe 17 | 18 | var log = logging.MustGetLogger("universe") 19 | 20 | func main() { 21 | fmt.Println("Running Hub Server") 22 | conf := util.GetConfig("universe.toml") 23 | // create the local datastore 24 | u = AndLetThereBeLight(conf) 25 | fmt.Println(u) 26 | // spin up the universe 27 | u.run() 28 | // set up the templates 29 | r := gin.Default() 30 | u.LoadTemplates() 31 | r.SetHTMLTemplate(u.templ) 32 | 33 | r.GET("/static/*filepath", u.staticFiles) 34 | r.GET("/", u.index) 35 | r.GET("/ws", func(c *gin.Context) { 36 | u.wshandler(c.Writer, c.Request) 37 | }) 38 | r.GET("/asset/*path", u.asset) 39 | r.Run(":8090") 40 | } 41 | 42 | func (u *universe) index(c *gin.Context) { 43 | c.HTML(200, "index.html", nil) 44 | } 45 | 46 | func (u *universe) asset(c *gin.Context) { 47 | // send to asset manager 48 | path := c.Params.ByName("path") 49 | data, err := u.cache.Cat(path) 50 | if err != nil { 51 | c.String(500, err.Error()) 52 | } 53 | c.Data(200, "", data) 54 | } 55 | 56 | func (u *universe) staticFiles(c *gin.Context) { 57 | static, err := rice.FindBox("static") 58 | if err != nil { 59 | fmt.Println("Static Error") 60 | } 61 | original := c.Request.URL.Path 62 | c.Request.URL.Path = c.Params.ByName("filepath") 63 | http.FileServer(static.HTTPBox()).ServeHTTP(c.Writer, c.Request) 64 | c.Request.URL.Path = original 65 | } 66 | 67 | var wsupgrader = websocket.Upgrader{ 68 | ReadBufferSize: 1024, 69 | WriteBufferSize: 1024, 70 | } 71 | 72 | func (u *universe) wshandler(w http.ResponseWriter, r *http.Request) { 73 | conn, err := wsupgrader.Upgrade(w, r, nil) 74 | if err != nil { 75 | fmt.Println(err) 76 | } 77 | c := NewConnection(u.h, conn) 78 | u.h.register <- c 79 | //world.register <- c 80 | // todo , move this to write pump and push a new player 81 | go c.writePump() 82 | c.readPump() 83 | } 84 | 85 | func (u *universe) LoadTemplates() { 86 | templateBox, err := rice.FindBox("templates") 87 | if err != nil { 88 | log.Critical("template fail ", err) 89 | } 90 | // collect all the templates 91 | fileList := []string{} 92 | visit := func(path string, f os.FileInfo, inerr error) (err error) { 93 | fmt.Println(path) 94 | if f.IsDir() == false { 95 | fileList = append(fileList, path) 96 | } 97 | return nil 98 | } 99 | walkerr := templateBox.Walk("", visit) 100 | if walkerr != nil { 101 | log.Critical("walk %s", err) 102 | } 103 | templates := template.New("") 104 | fmt.Println(fileList) 105 | for _, x := range fileList { 106 | templateString, err := templateBox.String(x) 107 | if err != nil { 108 | log.Fatal(err) 109 | } 110 | _, err = templates.New(x).Parse(templateString) 111 | if err != nil { 112 | log.Fatal(err) 113 | } 114 | } 115 | u.templ = templates 116 | } 117 | -------------------------------------------------------------------------------- /static/css/app.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | 6 | body { 7 | background-color: #ffffff; 8 | margin: 0; 9 | overflow: hidden; 10 | font-family: arial; 11 | } 12 | 13 | #blocker { 14 | 15 | position: absolute; 16 | 17 | width: 100%; 18 | height: 100%; 19 | 20 | background-color: rgb(0,0,0); 21 | 22 | } 23 | 24 | #instructions { 25 | 26 | width: 100%; 27 | height: 100%; 28 | 29 | display: -webkit-box; 30 | display: -moz-box; 31 | display: box; 32 | 33 | -webkit-box-orient: horizontal; 34 | -moz-box-orient: horizontal; 35 | box-orient: horizontal; 36 | 37 | -webkit-box-pack: center; 38 | -moz-box-pack: center; 39 | box-pack: center; 40 | 41 | -webkit-box-align: center; 42 | -moz-box-align: center; 43 | box-align: center; 44 | 45 | color: #ffffff; 46 | text-align: center; 47 | 48 | cursor: pointer; 49 | 50 | } 51 | -------------------------------------------------------------------------------- /static/images/dirt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zignig/cohort/2ab3e7d853b015829c385e98066ac84526395be1/static/images/dirt.jpg -------------------------------------------------------------------------------- /static/images/floor.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zignig/cohort/2ab3e7d853b015829c385e98066ac84526395be1/static/images/floor.jpg -------------------------------------------------------------------------------- /static/js/FlyControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author James Baicoianu / http://www.baicoianu.com/ 3 | */ 4 | 5 | THREE.FlyControls = function ( object, domElement ) { 6 | 7 | this.object = object; 8 | 9 | this.domElement = ( domElement !== undefined ) ? domElement : document; 10 | if ( domElement ) this.domElement.setAttribute( 'tabindex', -1 ); 11 | 12 | // API 13 | 14 | this.movementSpeed = 1.0; 15 | this.rollSpeed = 0.005; 16 | 17 | this.dragToLook = false; 18 | this.autoForward = false; 19 | 20 | // disable default target object behavior 21 | 22 | // internals 23 | 24 | this.tmpQuaternion = new THREE.Quaternion(); 25 | 26 | this.mouseStatus = 0; 27 | 28 | this.moveState = { up: 0, down: 0, left: 0, right: 0, forward: 0, back: 0, pitchUp: 0, pitchDown: 0, yawLeft: 0, yawRight: 0, rollLeft: 0, rollRight: 0 }; 29 | this.moveVector = new THREE.Vector3( 0, 0, 0 ); 30 | this.rotationVector = new THREE.Vector3( 0, 0, 0 ); 31 | 32 | this.handleEvent = function ( event ) { 33 | 34 | if ( typeof this[ event.type ] == 'function' ) { 35 | 36 | this[ event.type ]( event ); 37 | 38 | } 39 | 40 | }; 41 | 42 | this.keydown = function( event ) { 43 | 44 | if ( event.altKey ) { 45 | 46 | return; 47 | 48 | } 49 | 50 | //event.preventDefault(); 51 | 52 | switch ( event.keyCode ) { 53 | 54 | case 16: /* shift */ this.movementSpeedMultiplier = .1; break; 55 | 56 | case 87: /*W*/ this.moveState.forward = 1; break; 57 | case 83: /*S*/ this.moveState.back = 1; break; 58 | 59 | case 65: /*A*/ this.moveState.left = 1; break; 60 | case 68: /*D*/ this.moveState.right = 1; break; 61 | 62 | case 82: /*R*/ this.moveState.up = 1; break; 63 | case 70: /*F*/ this.moveState.down = 1; break; 64 | 65 | case 38: /*up*/ this.moveState.pitchUp = 1; break; 66 | case 40: /*down*/ this.moveState.pitchDown = 1; break; 67 | 68 | case 37: /*left*/ this.moveState.yawLeft = 1; break; 69 | case 39: /*right*/ this.moveState.yawRight = 1; break; 70 | 71 | case 81: /*Q*/ this.moveState.rollLeft = 1; break; 72 | case 69: /*E*/ this.moveState.rollRight = 1; break; 73 | 74 | } 75 | 76 | this.updateMovementVector(); 77 | this.updateRotationVector(); 78 | 79 | }; 80 | 81 | this.keyup = function( event ) { 82 | 83 | switch( event.keyCode ) { 84 | 85 | case 16: /* shift */ this.movementSpeedMultiplier = 1; break; 86 | 87 | case 87: /*W*/ this.moveState.forward = 0; break; 88 | case 83: /*S*/ this.moveState.back = 0; break; 89 | 90 | case 65: /*A*/ this.moveState.left = 0; break; 91 | case 68: /*D*/ this.moveState.right = 0; break; 92 | 93 | case 82: /*R*/ this.moveState.up = 0; break; 94 | case 70: /*F*/ this.moveState.down = 0; break; 95 | 96 | case 38: /*up*/ this.moveState.pitchUp = 0; break; 97 | case 40: /*down*/ this.moveState.pitchDown = 0; break; 98 | 99 | case 37: /*left*/ this.moveState.yawLeft = 0; break; 100 | case 39: /*right*/ this.moveState.yawRight = 0; break; 101 | 102 | case 81: /*Q*/ this.moveState.rollLeft = 0; break; 103 | case 69: /*E*/ this.moveState.rollRight = 0; break; 104 | 105 | } 106 | 107 | this.updateMovementVector(); 108 | this.updateRotationVector(); 109 | 110 | }; 111 | 112 | this.mousedown = function( event ) { 113 | 114 | if ( this.domElement !== document ) { 115 | 116 | this.domElement.focus(); 117 | 118 | } 119 | 120 | event.preventDefault(); 121 | event.stopPropagation(); 122 | 123 | if ( this.dragToLook ) { 124 | 125 | this.mouseStatus ++; 126 | 127 | } else { 128 | 129 | switch ( event.button ) { 130 | 131 | case 0: this.moveState.forward = 1; break; 132 | case 2: this.moveState.back = 1; break; 133 | 134 | } 135 | 136 | this.updateMovementVector(); 137 | 138 | } 139 | 140 | }; 141 | 142 | this.mousemove = function( event ) { 143 | 144 | if ( !this.dragToLook || this.mouseStatus > 0 ) { 145 | 146 | var container = this.getContainerDimensions(); 147 | var halfWidth = container.size[ 0 ] / 2; 148 | var halfHeight = container.size[ 1 ] / 2; 149 | 150 | this.moveState.yawLeft = - ( ( event.pageX - container.offset[ 0 ] ) - halfWidth ) / halfWidth; 151 | this.moveState.pitchDown = ( ( event.pageY - container.offset[ 1 ] ) - halfHeight ) / halfHeight; 152 | 153 | this.updateRotationVector(); 154 | 155 | } 156 | 157 | }; 158 | 159 | this.mouseup = function( event ) { 160 | 161 | event.preventDefault(); 162 | event.stopPropagation(); 163 | 164 | if ( this.dragToLook ) { 165 | 166 | this.mouseStatus --; 167 | 168 | this.moveState.yawLeft = this.moveState.pitchDown = 0; 169 | 170 | } else { 171 | 172 | switch ( event.button ) { 173 | 174 | case 0: this.moveState.forward = 0; break; 175 | case 2: this.moveState.back = 0; break; 176 | 177 | } 178 | 179 | this.updateMovementVector(); 180 | 181 | } 182 | 183 | this.updateRotationVector(); 184 | 185 | }; 186 | 187 | this.update = function( delta ) { 188 | 189 | var moveMult = delta * this.movementSpeed; 190 | var rotMult = delta * this.rollSpeed; 191 | 192 | this.object.translateX( this.moveVector.x * moveMult ); 193 | this.object.translateY( this.moveVector.y * moveMult ); 194 | this.object.translateZ( this.moveVector.z * moveMult ); 195 | 196 | this.tmpQuaternion.set( this.rotationVector.x * rotMult, this.rotationVector.y * rotMult, this.rotationVector.z * rotMult, 1 ).normalize(); 197 | this.object.quaternion.multiply( this.tmpQuaternion ); 198 | 199 | // expose the rotation vector for convenience 200 | this.object.rotation.setFromQuaternion( this.object.quaternion, this.object.rotation.order ); 201 | 202 | 203 | }; 204 | 205 | this.updateMovementVector = function() { 206 | 207 | var forward = ( this.moveState.forward || ( this.autoForward && !this.moveState.back ) ) ? 1 : 0; 208 | 209 | this.moveVector.x = ( -this.moveState.left + this.moveState.right ); 210 | this.moveVector.y = ( -this.moveState.down + this.moveState.up ); 211 | this.moveVector.z = ( -forward + this.moveState.back ); 212 | 213 | //console.log( 'move:', [ this.moveVector.x, this.moveVector.y, this.moveVector.z ] ); 214 | 215 | }; 216 | 217 | this.updateRotationVector = function() { 218 | 219 | this.rotationVector.x = ( -this.moveState.pitchDown + this.moveState.pitchUp ); 220 | this.rotationVector.y = ( -this.moveState.yawRight + this.moveState.yawLeft ); 221 | this.rotationVector.z = ( -this.moveState.rollRight + this.moveState.rollLeft ); 222 | 223 | //console.log( 'rotate:', [ this.rotationVector.x, this.rotationVector.y, this.rotationVector.z ] ); 224 | 225 | }; 226 | 227 | this.getContainerDimensions = function() { 228 | 229 | if ( this.domElement != document ) { 230 | 231 | return { 232 | size : [ this.domElement.offsetWidth, this.domElement.offsetHeight ], 233 | offset : [ this.domElement.offsetLeft, this.domElement.offsetTop ] 234 | }; 235 | 236 | } else { 237 | 238 | return { 239 | size : [ window.innerWidth, window.innerHeight ], 240 | offset : [ 0, 0 ] 241 | }; 242 | 243 | } 244 | 245 | }; 246 | 247 | function bind( scope, fn ) { 248 | 249 | return function () { 250 | 251 | fn.apply( scope, arguments ); 252 | 253 | }; 254 | 255 | }; 256 | 257 | this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); 258 | 259 | this.domElement.addEventListener( 'mousemove', bind( this, this.mousemove ), false ); 260 | this.domElement.addEventListener( 'mousedown', bind( this, this.mousedown ), false ); 261 | this.domElement.addEventListener( 'mouseup', bind( this, this.mouseup ), false ); 262 | 263 | window.addEventListener( 'keydown', bind( this, this.keydown ), false ); 264 | window.addEventListener( 'keyup', bind( this, this.keyup ), false ); 265 | 266 | this.updateMovementVector(); 267 | this.updateRotationVector(); 268 | 269 | }; 270 | -------------------------------------------------------------------------------- /static/js/MTLLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Loads a Wavefront .mtl file specifying materials 3 | * 4 | * @author angelxuanchang 5 | */ 6 | 7 | THREE.MTLLoader = function( baseUrl, options, crossOrigin ) { 8 | 9 | this.baseUrl = baseUrl; 10 | this.options = options; 11 | this.crossOrigin = crossOrigin; 12 | 13 | }; 14 | 15 | THREE.MTLLoader.prototype = { 16 | 17 | constructor: THREE.MTLLoader, 18 | 19 | load: function ( url, onLoad, onProgress, onError ) { 20 | 21 | var scope = this; 22 | 23 | var loader = new THREE.XHRLoader(); 24 | loader.setCrossOrigin( this.crossOrigin ); 25 | loader.load( url, function ( text ) { 26 | 27 | onLoad( scope.parse( text ) ); 28 | 29 | }, onProgress, onError ); 30 | 31 | }, 32 | 33 | /** 34 | * Parses loaded MTL file 35 | * @param text - Content of MTL file 36 | * @return {THREE.MTLLoader.MaterialCreator} 37 | */ 38 | parse: function ( text ) { 39 | 40 | var lines = text.split( "\n" ); 41 | var info = {}; 42 | var delimiter_pattern = /\s+/; 43 | var materialsInfo = {}; 44 | 45 | for ( var i = 0; i < lines.length; i ++ ) { 46 | 47 | var line = lines[ i ]; 48 | line = line.trim(); 49 | 50 | if ( line.length === 0 || line.charAt( 0 ) === '#' ) { 51 | 52 | // Blank line or comment ignore 53 | continue; 54 | 55 | } 56 | 57 | var pos = line.indexOf( ' ' ); 58 | 59 | var key = ( pos >= 0 ) ? line.substring( 0, pos ) : line; 60 | key = key.toLowerCase(); 61 | 62 | var value = ( pos >= 0 ) ? line.substring( pos + 1 ) : ""; 63 | value = value.trim(); 64 | 65 | if ( key === "newmtl" ) { 66 | 67 | // New material 68 | 69 | info = { name: value }; 70 | materialsInfo[ value ] = info; 71 | 72 | } else if ( info ) { 73 | 74 | if ( key === "ka" || key === "kd" || key === "ks" ) { 75 | 76 | var ss = value.split( delimiter_pattern, 3 ); 77 | info[ key ] = [ parseFloat( ss[0] ), parseFloat( ss[1] ), parseFloat( ss[2] ) ]; 78 | 79 | } else { 80 | 81 | info[ key ] = value; 82 | 83 | } 84 | 85 | } 86 | 87 | } 88 | 89 | var materialCreator = new THREE.MTLLoader.MaterialCreator( this.baseUrl, this.options ); 90 | materialCreator.setMaterials( materialsInfo ); 91 | return materialCreator; 92 | 93 | } 94 | 95 | }; 96 | 97 | /** 98 | * Create a new THREE-MTLLoader.MaterialCreator 99 | * @param baseUrl - Url relative to which textures are loaded 100 | * @param options - Set of options on how to construct the materials 101 | * side: Which side to apply the material 102 | * THREE.FrontSide (default), THREE.BackSide, THREE.DoubleSide 103 | * wrap: What type of wrapping to apply for textures 104 | * THREE.RepeatWrapping (default), THREE.ClampToEdgeWrapping, THREE.MirroredRepeatWrapping 105 | * normalizeRGB: RGBs need to be normalized to 0-1 from 0-255 106 | * Default: false, assumed to be already normalized 107 | * ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's 108 | * Default: false 109 | * invertTransparency: If transparency need to be inverted (inversion is needed if d = 0 is fully opaque) 110 | * Default: false (d = 1 is fully opaque) 111 | * @constructor 112 | */ 113 | 114 | THREE.MTLLoader.MaterialCreator = function( baseUrl, options ) { 115 | 116 | this.baseUrl = baseUrl; 117 | this.options = options; 118 | this.materialsInfo = {}; 119 | this.materials = {}; 120 | this.materialsArray = []; 121 | this.nameLookup = {}; 122 | 123 | this.side = ( this.options && this.options.side )? this.options.side: THREE.FrontSide; 124 | this.wrap = ( this.options && this.options.wrap )? this.options.wrap: THREE.RepeatWrapping; 125 | 126 | }; 127 | 128 | THREE.MTLLoader.MaterialCreator.prototype = { 129 | 130 | constructor: THREE.MTLLoader.MaterialCreator, 131 | 132 | setMaterials: function( materialsInfo ) { 133 | 134 | this.materialsInfo = this.convert( materialsInfo ); 135 | this.materials = {}; 136 | this.materialsArray = []; 137 | this.nameLookup = {}; 138 | 139 | }, 140 | 141 | convert: function( materialsInfo ) { 142 | 143 | if ( !this.options ) return materialsInfo; 144 | 145 | var converted = {}; 146 | 147 | for ( var mn in materialsInfo ) { 148 | 149 | // Convert materials info into normalized form based on options 150 | 151 | var mat = materialsInfo[ mn ]; 152 | 153 | var covmat = {}; 154 | 155 | converted[ mn ] = covmat; 156 | 157 | for ( var prop in mat ) { 158 | 159 | var save = true; 160 | var value = mat[ prop ]; 161 | var lprop = prop.toLowerCase(); 162 | 163 | switch ( lprop ) { 164 | 165 | case 'kd': 166 | case 'ka': 167 | case 'ks': 168 | 169 | // Diffuse color (color under white light) using RGB values 170 | 171 | if ( this.options && this.options.normalizeRGB ) { 172 | 173 | value = [ value[ 0 ] / 255, value[ 1 ] / 255, value[ 2 ] / 255 ]; 174 | 175 | } 176 | 177 | if ( this.options && this.options.ignoreZeroRGBs ) { 178 | 179 | if ( value[ 0 ] === 0 && value[ 1 ] === 0 && value[ 1 ] === 0 ) { 180 | 181 | // ignore 182 | 183 | save = false; 184 | 185 | } 186 | } 187 | 188 | break; 189 | 190 | case 'd': 191 | 192 | // According to MTL format (http://paulbourke.net/dataformats/mtl/): 193 | // d is dissolve for current material 194 | // factor of 1.0 is fully opaque, a factor of 0 is fully dissolved (completely transparent) 195 | 196 | if ( this.options && this.options.invertTransparency ) { 197 | 198 | value = 1 - value; 199 | 200 | } 201 | 202 | break; 203 | 204 | default: 205 | 206 | break; 207 | } 208 | 209 | if ( save ) { 210 | 211 | covmat[ lprop ] = value; 212 | 213 | } 214 | 215 | } 216 | 217 | } 218 | 219 | return converted; 220 | 221 | }, 222 | 223 | preload: function () { 224 | 225 | for ( var mn in this.materialsInfo ) { 226 | 227 | this.create( mn ); 228 | 229 | } 230 | 231 | }, 232 | 233 | getIndex: function( materialName ) { 234 | 235 | return this.nameLookup[ materialName ]; 236 | 237 | }, 238 | 239 | getAsArray: function() { 240 | 241 | var index = 0; 242 | 243 | for ( var mn in this.materialsInfo ) { 244 | 245 | this.materialsArray[ index ] = this.create( mn ); 246 | this.nameLookup[ mn ] = index; 247 | index ++; 248 | 249 | } 250 | 251 | return this.materialsArray; 252 | 253 | }, 254 | 255 | create: function ( materialName ) { 256 | 257 | if ( this.materials[ materialName ] === undefined ) { 258 | 259 | this.createMaterial_( materialName ); 260 | 261 | } 262 | 263 | return this.materials[ materialName ]; 264 | 265 | }, 266 | 267 | createMaterial_: function ( materialName ) { 268 | 269 | // Create material 270 | 271 | var mat = this.materialsInfo[ materialName ]; 272 | var params = { 273 | 274 | name: materialName, 275 | side: this.side 276 | 277 | }; 278 | 279 | for ( var prop in mat ) { 280 | 281 | var value = mat[ prop ]; 282 | 283 | switch ( prop.toLowerCase() ) { 284 | 285 | // Ns is material specular exponent 286 | 287 | case 'kd': 288 | 289 | // Diffuse color (color under white light) using RGB values 290 | 291 | params[ 'diffuse' ] = new THREE.Color().fromArray( value ); 292 | 293 | break; 294 | 295 | case 'ka': 296 | 297 | // Ambient color (color under shadow) using RGB values 298 | 299 | params[ 'ambient' ] = new THREE.Color().fromArray( value ); 300 | 301 | break; 302 | 303 | case 'ks': 304 | 305 | // Specular color (color when light is reflected from shiny surface) using RGB values 306 | params[ 'specular' ] = new THREE.Color().fromArray( value ); 307 | 308 | break; 309 | 310 | case 'map_kd': 311 | 312 | // Diffuse texture map 313 | 314 | params[ 'map' ] = this.loadTexture( this.baseUrl + value ); 315 | params[ 'map' ].wrapS = this.wrap; 316 | params[ 'map' ].wrapT = this.wrap; 317 | 318 | break; 319 | 320 | case 'ns': 321 | 322 | // The specular exponent (defines the focus of the specular highlight) 323 | // A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000. 324 | 325 | params['shininess'] = value; 326 | 327 | break; 328 | 329 | case 'd': 330 | 331 | // According to MTL format (http://paulbourke.net/dataformats/mtl/): 332 | // d is dissolve for current material 333 | // factor of 1.0 is fully opaque, a factor of 0 is fully dissolved (completely transparent) 334 | 335 | if ( value < 1 ) { 336 | 337 | params['transparent'] = true; 338 | params['opacity'] = value; 339 | 340 | } 341 | 342 | break; 343 | 344 | default: 345 | break; 346 | 347 | } 348 | 349 | } 350 | 351 | if ( params[ 'diffuse' ] ) { 352 | 353 | if ( !params[ 'ambient' ]) params[ 'ambient' ] = params[ 'diffuse' ]; 354 | params[ 'color' ] = params[ 'diffuse' ]; 355 | 356 | } 357 | 358 | this.materials[ materialName ] = new THREE.MeshPhongMaterial( params ); 359 | return this.materials[ materialName ]; 360 | 361 | }, 362 | 363 | 364 | loadTexture: function ( url, mapping, onLoad, onError ) { 365 | 366 | var texture; 367 | var loader = THREE.Loader.Handlers.get( url ); 368 | 369 | if ( loader !== null ) { 370 | 371 | texture = loader.load( url, onLoad ); 372 | 373 | } else { 374 | 375 | texture = new THREE.Texture(); 376 | 377 | loader = new THREE.ImageLoader(); 378 | loader.crossOrigin = this.crossOrigin; 379 | loader.load( url, function ( image ) { 380 | 381 | texture.image = THREE.MTLLoader.ensurePowerOfTwo_( image ); 382 | texture.needsUpdate = true; 383 | 384 | if ( onLoad ) onLoad( texture ); 385 | 386 | } ); 387 | 388 | } 389 | 390 | texture.mapping = mapping; 391 | 392 | return texture; 393 | 394 | } 395 | 396 | }; 397 | 398 | THREE.MTLLoader.ensurePowerOfTwo_ = function ( image ) { 399 | 400 | if ( ! THREE.Math.isPowerOfTwo( image.width ) || ! THREE.Math.isPowerOfTwo( image.height ) ) { 401 | 402 | var canvas = document.createElement( "canvas" ); 403 | canvas.width = THREE.MTLLoader.nextHighestPowerOfTwo_( image.width ); 404 | canvas.height = THREE.MTLLoader.nextHighestPowerOfTwo_( image.height ); 405 | 406 | var ctx = canvas.getContext("2d"); 407 | ctx.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height ); 408 | return canvas; 409 | 410 | } 411 | 412 | return image; 413 | 414 | }; 415 | 416 | THREE.MTLLoader.nextHighestPowerOfTwo_ = function( x ) { 417 | 418 | --x; 419 | 420 | for ( var i = 1; i < 32; i <<= 1 ) { 421 | 422 | x = x | x >> i; 423 | 424 | } 425 | 426 | return x + 1; 427 | 428 | }; 429 | 430 | THREE.EventDispatcher.prototype.apply( THREE.MTLLoader.prototype ); 431 | -------------------------------------------------------------------------------- /static/js/OBJLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | 5 | THREE.OBJLoader = function ( manager ) { 6 | 7 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; 8 | 9 | }; 10 | 11 | THREE.OBJLoader.prototype = { 12 | 13 | constructor: THREE.OBJLoader, 14 | 15 | load: function ( url, onLoad, onProgress, onError ) { 16 | 17 | var scope = this; 18 | 19 | var loader = new THREE.XHRLoader( scope.manager ); 20 | loader.setCrossOrigin( this.crossOrigin ); 21 | loader.load( url, function ( text ) { 22 | 23 | onLoad( scope.parse( text ) ); 24 | 25 | }, onProgress, onError ); 26 | 27 | }, 28 | 29 | parse: function ( text ) { 30 | 31 | console.time( 'OBJLoader' ); 32 | 33 | var object, objects = []; 34 | var geometry, material; 35 | 36 | function parseVertexIndex( value ) { 37 | 38 | var index = parseInt( value ); 39 | 40 | return ( index >= 0 ? index - 1 : index + vertices.length / 3 ) * 3; 41 | 42 | } 43 | 44 | function parseNormalIndex( value ) { 45 | 46 | var index = parseInt( value ); 47 | 48 | return ( index >= 0 ? index - 1 : index + normals.length / 3 ) * 3; 49 | 50 | } 51 | 52 | function parseUVIndex( value ) { 53 | 54 | var index = parseInt( value ); 55 | 56 | return ( index >= 0 ? index - 1 : index + uvs.length / 2 ) * 2; 57 | 58 | } 59 | 60 | function addVertex( a, b, c ) { 61 | 62 | geometry.vertices.push( 63 | vertices[ a ], vertices[ a + 1 ], vertices[ a + 2 ], 64 | vertices[ b ], vertices[ b + 1 ], vertices[ b + 2 ], 65 | vertices[ c ], vertices[ c + 1 ], vertices[ c + 2 ] 66 | ); 67 | 68 | } 69 | 70 | function addNormal( a, b, c ) { 71 | 72 | geometry.normals.push( 73 | normals[ a ], normals[ a + 1 ], normals[ a + 2 ], 74 | normals[ b ], normals[ b + 1 ], normals[ b + 2 ], 75 | normals[ c ], normals[ c + 1 ], normals[ c + 2 ] 76 | ); 77 | 78 | } 79 | 80 | function addUV( a, b, c ) { 81 | 82 | geometry.uvs.push( 83 | uvs[ a ], uvs[ a + 1 ], 84 | uvs[ b ], uvs[ b + 1 ], 85 | uvs[ c ], uvs[ c + 1 ] 86 | ); 87 | 88 | } 89 | 90 | function addFace( a, b, c, d, ua, ub, uc, ud, na, nb, nc, nd ) { 91 | 92 | var ia = parseVertexIndex( a ); 93 | var ib = parseVertexIndex( b ); 94 | var ic = parseVertexIndex( c ); 95 | 96 | if ( d === undefined ) { 97 | 98 | addVertex( ia, ib, ic ); 99 | 100 | } else { 101 | 102 | var id = parseVertexIndex( d ); 103 | 104 | addVertex( ia, ib, id ); 105 | addVertex( ib, ic, id ); 106 | 107 | } 108 | 109 | if ( ua !== undefined ) { 110 | 111 | var ia = parseUVIndex( ua ); 112 | var ib = parseUVIndex( ub ); 113 | var ic = parseUVIndex( uc ); 114 | 115 | if ( d === undefined ) { 116 | 117 | addUV( ia, ib, ic ); 118 | 119 | } else { 120 | 121 | var id = parseUVIndex( ud ); 122 | 123 | addUV( ia, ib, id ); 124 | addUV( ib, ic, id ); 125 | 126 | } 127 | 128 | } 129 | 130 | if ( na !== undefined ) { 131 | 132 | var ia = parseNormalIndex( na ); 133 | var ib = parseNormalIndex( nb ); 134 | var ic = parseNormalIndex( nc ); 135 | 136 | if ( d === undefined ) { 137 | 138 | addNormal( ia, ib, ic ); 139 | 140 | } else { 141 | 142 | var id = parseNormalIndex( nd ); 143 | 144 | addNormal( ia, ib, id ); 145 | addNormal( ib, ic, id ); 146 | 147 | } 148 | 149 | } 150 | 151 | } 152 | 153 | // create mesh if no objects in text 154 | 155 | if ( /^o /gm.test( text ) === false ) { 156 | 157 | geometry = { 158 | vertices: [], 159 | normals: [], 160 | uvs: [] 161 | }; 162 | 163 | material = { 164 | name: '' 165 | }; 166 | 167 | object = { 168 | name: '', 169 | geometry: geometry, 170 | material: material 171 | }; 172 | 173 | objects.push( object ); 174 | 175 | } 176 | 177 | var vertices = []; 178 | var normals = []; 179 | var uvs = []; 180 | 181 | // v float float float 182 | 183 | var vertex_pattern = /v( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; 184 | 185 | // vn float float float 186 | 187 | var normal_pattern = /vn( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; 188 | 189 | // vt float float 190 | 191 | var uv_pattern = /vt( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; 192 | 193 | // f vertex vertex vertex ... 194 | 195 | var face_pattern1 = /f( +-?\d+)( +-?\d+)( +-?\d+)( +-?\d+)?/; 196 | 197 | // f vertex/uv vertex/uv vertex/uv ... 198 | 199 | var face_pattern2 = /f( +(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+))?/; 200 | 201 | // f vertex/uv/normal vertex/uv/normal vertex/uv/normal ... 202 | 203 | var face_pattern3 = /f( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))?/; 204 | 205 | // f vertex//normal vertex//normal vertex//normal ... 206 | 207 | var face_pattern4 = /f( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))?/ 208 | 209 | // 210 | 211 | var lines = text.split( '\n' ); 212 | 213 | for ( var i = 0; i < lines.length; i ++ ) { 214 | 215 | var line = lines[ i ]; 216 | line = line.trim(); 217 | 218 | var result; 219 | 220 | if ( line.length === 0 || line.charAt( 0 ) === '#' ) { 221 | 222 | continue; 223 | 224 | } else if ( ( result = vertex_pattern.exec( line ) ) !== null ) { 225 | 226 | // ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"] 227 | 228 | vertices.push( 229 | parseFloat( result[ 1 ] ), 230 | parseFloat( result[ 2 ] ), 231 | parseFloat( result[ 3 ] ) 232 | ); 233 | 234 | } else if ( ( result = normal_pattern.exec( line ) ) !== null ) { 235 | 236 | // ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"] 237 | 238 | normals.push( 239 | parseFloat( result[ 1 ] ), 240 | parseFloat( result[ 2 ] ), 241 | parseFloat( result[ 3 ] ) 242 | ); 243 | 244 | } else if ( ( result = uv_pattern.exec( line ) ) !== null ) { 245 | 246 | // ["vt 0.1 0.2", "0.1", "0.2"] 247 | 248 | uvs.push( 249 | parseFloat( result[ 1 ] ), 250 | parseFloat( result[ 2 ] ) 251 | ); 252 | 253 | } else if ( ( result = face_pattern1.exec( line ) ) !== null ) { 254 | 255 | // ["f 1 2 3", "1", "2", "3", undefined] 256 | 257 | addFace( 258 | result[ 1 ], result[ 2 ], result[ 3 ], result[ 4 ] 259 | ); 260 | 261 | } else if ( ( result = face_pattern2.exec( line ) ) !== null ) { 262 | 263 | // ["f 1/1 2/2 3/3", " 1/1", "1", "1", " 2/2", "2", "2", " 3/3", "3", "3", undefined, undefined, undefined] 264 | 265 | addFace( 266 | result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ], 267 | result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] 268 | ); 269 | 270 | } else if ( ( result = face_pattern3.exec( line ) ) !== null ) { 271 | 272 | // ["f 1/1/1 2/2/2 3/3/3", " 1/1/1", "1", "1", "1", " 2/2/2", "2", "2", "2", " 3/3/3", "3", "3", "3", undefined, undefined, undefined, undefined] 273 | 274 | addFace( 275 | result[ 2 ], result[ 6 ], result[ 10 ], result[ 14 ], 276 | result[ 3 ], result[ 7 ], result[ 11 ], result[ 15 ], 277 | result[ 4 ], result[ 8 ], result[ 12 ], result[ 16 ] 278 | ); 279 | 280 | } else if ( ( result = face_pattern4.exec( line ) ) !== null ) { 281 | 282 | // ["f 1//1 2//2 3//3", " 1//1", "1", "1", " 2//2", "2", "2", " 3//3", "3", "3", undefined, undefined, undefined] 283 | 284 | addFace( 285 | result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ], 286 | undefined, undefined, undefined, undefined, 287 | result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] 288 | ); 289 | 290 | } else if ( /^o /.test( line ) ) { 291 | 292 | geometry = { 293 | vertices: [], 294 | normals: [], 295 | uvs: [] 296 | }; 297 | 298 | material = { 299 | name: '' 300 | }; 301 | 302 | object = { 303 | name: line.substring( 2 ).trim(), 304 | geometry: geometry, 305 | material: material 306 | }; 307 | 308 | objects.push( object ) 309 | 310 | } else if ( /^g /.test( line ) ) { 311 | 312 | // group 313 | 314 | } else if ( /^usemtl /.test( line ) ) { 315 | 316 | // material 317 | 318 | material.name = line.substring( 7 ).trim(); 319 | 320 | } else if ( /^mtllib /.test( line ) ) { 321 | 322 | // mtl file 323 | 324 | } else if ( /^s /.test( line ) ) { 325 | 326 | // smooth shading 327 | 328 | } else { 329 | 330 | // console.log( "THREE.OBJLoader: Unhandled line " + line ); 331 | 332 | } 333 | 334 | } 335 | 336 | var container = new THREE.Object3D(); 337 | 338 | for ( var i = 0, l = objects.length; i < l; i ++ ) { 339 | 340 | var object = objects[ i ]; 341 | var geometry = object.geometry; 342 | 343 | var buffergeometry = new THREE.BufferGeometry(); 344 | 345 | buffergeometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( geometry.vertices ), 3 ) ); 346 | 347 | if ( geometry.normals.length > 0 ) { 348 | buffergeometry.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( geometry.normals ), 3 ) ); 349 | } 350 | 351 | if ( geometry.uvs.length > 0 ) { 352 | buffergeometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( geometry.uvs ), 2 ) ); 353 | } 354 | 355 | var material = new THREE.MeshLambertMaterial(); 356 | material.name = object.material.name; 357 | 358 | var mesh = new THREE.Mesh( buffergeometry, material ); 359 | mesh.name = object.name; 360 | 361 | container.add( mesh ); 362 | 363 | } 364 | 365 | console.timeEnd( 'OBJLoader' ); 366 | 367 | return container; 368 | 369 | } 370 | 371 | }; 372 | -------------------------------------------------------------------------------- /static/js/OBJMTLLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Loads a Wavefront .obj file with materials 3 | * 4 | * @author mrdoob / http://mrdoob.com/ 5 | * @author angelxuanchang 6 | */ 7 | 8 | THREE.OBJMTLLoader = function ( manager ) { 9 | 10 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; 11 | 12 | }; 13 | 14 | THREE.OBJMTLLoader.prototype = { 15 | 16 | constructor: THREE.OBJMTLLoader, 17 | 18 | load: function ( url, mtlurl, onLoad, onProgress, onError ) { 19 | 20 | var scope = this; 21 | 22 | var mtlLoader = new THREE.MTLLoader( url.substr( 0, url.lastIndexOf( "/" ) + 1 ) ); 23 | mtlLoader.load( mtlurl, function ( materials ) { 24 | 25 | var materialsCreator = materials; 26 | materialsCreator.preload(); 27 | 28 | var loader = new THREE.XHRLoader( scope.manager ); 29 | loader.setCrossOrigin( this.crossOrigin ); 30 | loader.load( url, function ( text ) { 31 | 32 | var object = scope.parse( text ); 33 | 34 | object.traverse( function ( object ) { 35 | 36 | if ( object instanceof THREE.Mesh ) { 37 | 38 | if ( object.material.name ) { 39 | 40 | var material = materialsCreator.create( object.material.name ); 41 | 42 | if ( material ) object.material = material; 43 | 44 | } 45 | 46 | } 47 | 48 | } ); 49 | 50 | onLoad( object ); 51 | 52 | }, onProgress, onError ); 53 | 54 | }, onProgress, onError ); 55 | 56 | }, 57 | 58 | /** 59 | * Parses loaded .obj file 60 | * @param data - content of .obj file 61 | * @param mtllibCallback - callback to handle mtllib declaration (optional) 62 | * @return {THREE.Object3D} - Object3D (with default material) 63 | */ 64 | 65 | parse: function ( data, mtllibCallback ) { 66 | 67 | function vector( x, y, z ) { 68 | 69 | return new THREE.Vector3( x, y, z ); 70 | 71 | } 72 | 73 | function uv( u, v ) { 74 | 75 | return new THREE.Vector2( u, v ); 76 | 77 | } 78 | 79 | function face3( a, b, c, normals ) { 80 | 81 | return new THREE.Face3( a, b, c, normals ); 82 | 83 | } 84 | 85 | var face_offset = 0; 86 | 87 | function meshN( meshName, materialName ) { 88 | 89 | if ( vertices.length > 0 ) { 90 | 91 | geometry.vertices = vertices; 92 | 93 | geometry.mergeVertices(); 94 | geometry.computeFaceNormals(); 95 | geometry.computeBoundingSphere(); 96 | 97 | object.add( mesh ); 98 | 99 | geometry = new THREE.Geometry(); 100 | mesh = new THREE.Mesh( geometry, material ); 101 | 102 | } 103 | 104 | if ( meshName !== undefined ) mesh.name = meshName; 105 | 106 | if ( materialName !== undefined ) { 107 | 108 | material = new THREE.MeshLambertMaterial(); 109 | material.name = materialName; 110 | 111 | mesh.material = material; 112 | 113 | } 114 | 115 | } 116 | 117 | var group = new THREE.Group(); 118 | var object = group; 119 | 120 | var geometry = new THREE.Geometry(); 121 | var material = new THREE.MeshLambertMaterial(); 122 | var mesh = new THREE.Mesh( geometry, material ); 123 | 124 | var vertices = []; 125 | var normals = []; 126 | var uvs = []; 127 | 128 | function add_face( a, b, c, normals_inds ) { 129 | 130 | if ( normals_inds === undefined ) { 131 | 132 | geometry.faces.push( face3( 133 | parseInt( a ) - (face_offset + 1), 134 | parseInt( b ) - (face_offset + 1), 135 | parseInt( c ) - (face_offset + 1) 136 | ) ); 137 | 138 | } else { 139 | 140 | geometry.faces.push( face3( 141 | parseInt( a ) - (face_offset + 1), 142 | parseInt( b ) - (face_offset + 1), 143 | parseInt( c ) - (face_offset + 1), 144 | [ 145 | normals[ parseInt( normals_inds[ 0 ] ) - 1 ].clone(), 146 | normals[ parseInt( normals_inds[ 1 ] ) - 1 ].clone(), 147 | normals[ parseInt( normals_inds[ 2 ] ) - 1 ].clone() 148 | ] 149 | ) ); 150 | 151 | } 152 | 153 | } 154 | 155 | function add_uvs( a, b, c ) { 156 | 157 | geometry.faceVertexUvs[ 0 ].push( [ 158 | uvs[ parseInt( a ) - 1 ].clone(), 159 | uvs[ parseInt( b ) - 1 ].clone(), 160 | uvs[ parseInt( c ) - 1 ].clone() 161 | ] ); 162 | 163 | } 164 | 165 | function handle_face_line(faces, uvs, normals_inds) { 166 | 167 | if ( faces[ 3 ] === undefined ) { 168 | 169 | add_face( faces[ 0 ], faces[ 1 ], faces[ 2 ], normals_inds ); 170 | 171 | if (!(uvs === undefined) && uvs.length > 0) { 172 | add_uvs( uvs[ 0 ], uvs[ 1 ], uvs[ 2 ] ); 173 | } 174 | 175 | } else { 176 | 177 | if (!(normals_inds === undefined) && normals_inds.length > 0) { 178 | 179 | add_face( faces[ 0 ], faces[ 1 ], faces[ 3 ], [ normals_inds[ 0 ], normals_inds[ 1 ], normals_inds[ 3 ] ]); 180 | add_face( faces[ 1 ], faces[ 2 ], faces[ 3 ], [ normals_inds[ 1 ], normals_inds[ 2 ], normals_inds[ 3 ] ]); 181 | 182 | } else { 183 | 184 | add_face( faces[ 0 ], faces[ 1 ], faces[ 3 ]); 185 | add_face( faces[ 1 ], faces[ 2 ], faces[ 3 ]); 186 | 187 | } 188 | 189 | if (!(uvs === undefined) && uvs.length > 0) { 190 | 191 | add_uvs( uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ); 192 | add_uvs( uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ); 193 | 194 | } 195 | 196 | } 197 | 198 | } 199 | 200 | 201 | // v float float float 202 | 203 | var vertex_pattern = /v( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/; 204 | 205 | // vn float float float 206 | 207 | var normal_pattern = /vn( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/; 208 | 209 | // vt float float 210 | 211 | var uv_pattern = /vt( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/; 212 | 213 | // f vertex vertex vertex ... 214 | 215 | var face_pattern1 = /f( +\d+)( +\d+)( +\d+)( +\d+)?/; 216 | 217 | // f vertex/uv vertex/uv vertex/uv ... 218 | 219 | var face_pattern2 = /f( +(\d+)\/(\d+))( +(\d+)\/(\d+))( +(\d+)\/(\d+))( +(\d+)\/(\d+))?/; 220 | 221 | // f vertex/uv/normal vertex/uv/normal vertex/uv/normal ... 222 | 223 | var face_pattern3 = /f( +(\d+)\/(\d+)\/(\d+))( +(\d+)\/(\d+)\/(\d+))( +(\d+)\/(\d+)\/(\d+))( +(\d+)\/(\d+)\/(\d+))?/; 224 | 225 | // f vertex//normal vertex//normal vertex//normal ... 226 | 227 | var face_pattern4 = /f( +(\d+)\/\/(\d+))( +(\d+)\/\/(\d+))( +(\d+)\/\/(\d+))( +(\d+)\/\/(\d+))?/ 228 | 229 | // 230 | 231 | var lines = data.split( "\n" ); 232 | 233 | for ( var i = 0; i < lines.length; i ++ ) { 234 | 235 | var line = lines[ i ]; 236 | line = line.trim(); 237 | 238 | var result; 239 | 240 | if ( line.length === 0 || line.charAt( 0 ) === '#' ) { 241 | 242 | continue; 243 | 244 | } else if ( ( result = vertex_pattern.exec( line ) ) !== null ) { 245 | 246 | // ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"] 247 | 248 | vertices.push( vector( 249 | parseFloat( result[ 1 ] ), 250 | parseFloat( result[ 2 ] ), 251 | parseFloat( result[ 3 ] ) 252 | ) ); 253 | 254 | } else if ( ( result = normal_pattern.exec( line ) ) !== null ) { 255 | 256 | // ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"] 257 | 258 | normals.push( vector( 259 | parseFloat( result[ 1 ] ), 260 | parseFloat( result[ 2 ] ), 261 | parseFloat( result[ 3 ] ) 262 | ) ); 263 | 264 | } else if ( ( result = uv_pattern.exec( line ) ) !== null ) { 265 | 266 | // ["vt 0.1 0.2", "0.1", "0.2"] 267 | 268 | uvs.push( uv( 269 | parseFloat( result[ 1 ] ), 270 | parseFloat( result[ 2 ] ) 271 | ) ); 272 | 273 | } else if ( ( result = face_pattern1.exec( line ) ) !== null ) { 274 | 275 | // ["f 1 2 3", "1", "2", "3", undefined] 276 | 277 | handle_face_line([ result[ 1 ], result[ 2 ], result[ 3 ], result[ 4 ] ]); 278 | 279 | } else if ( ( result = face_pattern2.exec( line ) ) !== null ) { 280 | 281 | // ["f 1/1 2/2 3/3", " 1/1", "1", "1", " 2/2", "2", "2", " 3/3", "3", "3", undefined, undefined, undefined] 282 | 283 | handle_face_line( 284 | [ result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ] ], //faces 285 | [ result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] ] //uv 286 | ); 287 | 288 | } else if ( ( result = face_pattern3.exec( line ) ) !== null ) { 289 | 290 | // ["f 1/1/1 2/2/2 3/3/3", " 1/1/1", "1", "1", "1", " 2/2/2", "2", "2", "2", " 3/3/3", "3", "3", "3", undefined, undefined, undefined, undefined] 291 | 292 | handle_face_line( 293 | [ result[ 2 ], result[ 6 ], result[ 10 ], result[ 14 ] ], //faces 294 | [ result[ 3 ], result[ 7 ], result[ 11 ], result[ 15 ] ], //uv 295 | [ result[ 4 ], result[ 8 ], result[ 12 ], result[ 16 ] ] //normal 296 | ); 297 | 298 | } else if ( ( result = face_pattern4.exec( line ) ) !== null ) { 299 | 300 | // ["f 1//1 2//2 3//3", " 1//1", "1", "1", " 2//2", "2", "2", " 3//3", "3", "3", undefined, undefined, undefined] 301 | 302 | handle_face_line( 303 | [ result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ] ], //faces 304 | [ ], //uv 305 | [ result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] ] //normal 306 | ); 307 | 308 | } else if ( /^o /.test( line ) ) { 309 | 310 | // object 311 | 312 | meshN(); 313 | face_offset = face_offset + vertices.length; 314 | vertices = []; 315 | object = new THREE.Object3D(); 316 | object.name = line.substring( 2 ).trim(); 317 | group.add( object ); 318 | 319 | } else if ( /^g /.test( line ) ) { 320 | 321 | // group 322 | 323 | meshN( line.substring( 2 ).trim(), undefined ); 324 | 325 | } else if ( /^usemtl /.test( line ) ) { 326 | 327 | // material 328 | 329 | meshN( undefined, line.substring( 7 ).trim() ); 330 | 331 | } else if ( /^mtllib /.test( line ) ) { 332 | 333 | // mtl file 334 | 335 | if ( mtllibCallback ) { 336 | 337 | var mtlfile = line.substring( 7 ); 338 | mtlfile = mtlfile.trim(); 339 | mtllibCallback( mtlfile ); 340 | 341 | } 342 | 343 | } else if ( /^s /.test( line ) ) { 344 | 345 | // Smooth shading 346 | 347 | } else { 348 | 349 | console.log( "THREE.OBJMTLLoader: Unhandled line " + line ); 350 | 351 | } 352 | 353 | } 354 | 355 | //Add last object 356 | meshN(undefined, undefined); 357 | 358 | return group; 359 | 360 | } 361 | 362 | }; 363 | 364 | THREE.EventDispatcher.prototype.apply( THREE.OBJMTLLoader.prototype ); 365 | -------------------------------------------------------------------------------- /static/js/PointerLockControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | 5 | THREE.PointerLockControls = function ( camera ) { 6 | 7 | var scope = this; 8 | 9 | camera.rotation.set( 0, 0, 0 ); 10 | 11 | var pitchObject = new THREE.Object3D(); 12 | pitchObject.add( camera ); 13 | pitchObject.rotation.x = -0.2; 14 | 15 | var yawObject = new THREE.Object3D(); 16 | yawObject.position.y = 60; 17 | yawObject.rotation.y = 4.0; 18 | yawObject.add( pitchObject ); 19 | 20 | var moveForward = false; 21 | var moveBackward = false; 22 | var moveLeft = false; 23 | var moveRight = false; 24 | 25 | var isOnObject = false; 26 | var canJump = false; 27 | 28 | var prevTime = performance.now(); 29 | 30 | var velocity = new THREE.Vector3(); 31 | 32 | var PI_2 = Math.PI / 2; 33 | 34 | var onMouseMove = function ( event ) { 35 | 36 | if ( scope.enabled === false ) return; 37 | 38 | var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0; 39 | var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0; 40 | 41 | yawObject.rotation.y -= movementX * 0.002; 42 | pitchObject.rotation.x -= movementY * 0.002; 43 | 44 | pitchObject.rotation.x = Math.max( - PI_2, Math.min( PI_2, pitchObject.rotation.x ) ); 45 | 46 | }; 47 | 48 | var onKeyDown = function ( event ) { 49 | 50 | switch ( event.keyCode ) { 51 | 52 | case 38: // up 53 | case 87: // w 54 | moveForward = true; 55 | break; 56 | 57 | case 37: // left 58 | case 65: // a 59 | moveLeft = true; break; 60 | 61 | case 40: // down 62 | case 83: // s 63 | moveBackward = true; 64 | break; 65 | 66 | case 39: // right 67 | case 68: // d 68 | moveRight = true; 69 | break; 70 | 71 | case 32: // space 72 | if ( canJump === true ) velocity.y += 350; 73 | canJump = false; 74 | break; 75 | 76 | } 77 | 78 | }; 79 | 80 | var onKeyUp = function ( event ) { 81 | 82 | switch( event.keyCode ) { 83 | 84 | case 38: // up 85 | case 87: // w 86 | moveForward = false; 87 | break; 88 | 89 | case 37: // left 90 | case 65: // a 91 | moveLeft = false; 92 | break; 93 | 94 | case 40: // down 95 | case 83: // s 96 | moveBackward = false; 97 | break; 98 | 99 | case 39: // right 100 | case 68: // d 101 | moveRight = false; 102 | break; 103 | 104 | } 105 | 106 | }; 107 | 108 | document.addEventListener( 'mousemove', onMouseMove, false ); 109 | document.addEventListener( 'keydown', onKeyDown, false ); 110 | document.addEventListener( 'keyup', onKeyUp, false ); 111 | 112 | this.enabled = false; 113 | 114 | this.getObject = function () { 115 | 116 | return yawObject; 117 | 118 | }; 119 | 120 | this.isOnObject = function ( boolean ) { 121 | 122 | isOnObject = boolean; 123 | canJump = boolean; 124 | 125 | }; 126 | 127 | this.getDirection = function() { 128 | 129 | // assumes the camera itself is not rotated 130 | 131 | var direction = new THREE.Vector3( 0, 0, -1 ); 132 | var rotation = new THREE.Euler( 0, 0, 0, "YXZ" ); 133 | 134 | return function( v ) { 135 | 136 | rotation.set( pitchObject.rotation.x, yawObject.rotation.y, 0 ); 137 | 138 | v.copy( direction ).applyEuler( rotation ); 139 | 140 | return v; 141 | 142 | } 143 | 144 | }(); 145 | 146 | this.update = function () { 147 | 148 | if ( scope.enabled === false ) return; 149 | 150 | var time = performance.now(); 151 | var delta = ( time - prevTime ) / 1000; 152 | 153 | velocity.x -= velocity.x * 10.0 * delta; 154 | velocity.z -= velocity.z * 10.0 * delta; 155 | 156 | velocity.y -= 9.8 * 100.0 * delta; // 100.0 = mass 157 | 158 | if ( moveForward ) velocity.z -= 1400.0 * delta; 159 | if ( moveBackward ) velocity.z += 1400.0 * delta; 160 | 161 | if ( moveLeft ) velocity.x -= 1400.0 * delta; 162 | if ( moveRight ) velocity.x += 1400.0 * delta; 163 | 164 | if ( isOnObject === true ) { 165 | 166 | velocity.y = Math.max( 0, velocity.y ); 167 | 168 | } 169 | 170 | yawObject.translateX( velocity.x * delta ); 171 | yawObject.translateY( velocity.y * delta ); 172 | yawObject.translateZ( velocity.z * delta ); 173 | 174 | if ( yawObject.position.y < 50 ) { 175 | 176 | velocity.y = 0; 177 | yawObject.position.y = 50; 178 | 179 | canJump = true; 180 | 181 | } 182 | 183 | prevTime = time; 184 | 185 | }; 186 | 187 | }; 188 | -------------------------------------------------------------------------------- /static/js/SceneLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author alteredq / http://alteredqualia.com/ 3 | */ 4 | 5 | THREE.SceneLoader = function ( manager ) { 6 | 7 | this.onLoadStart = function () {}; 8 | this.onLoadProgress = function() {}; 9 | this.onLoadComplete = function () {}; 10 | 11 | this.callbackSync = function () {}; 12 | this.callbackProgress = function () {}; 13 | 14 | this.geometryHandlers = {}; 15 | this.hierarchyHandlers = {}; 16 | 17 | this.addGeometryHandler( "ascii", THREE.JSONLoader ); 18 | 19 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; 20 | 21 | }; 22 | 23 | THREE.SceneLoader.prototype = { 24 | 25 | constructor: THREE.SceneLoader, 26 | 27 | load: function ( url, onLoad, onProgress, onError ) { 28 | 29 | var scope = this; 30 | 31 | var loader = new THREE.XHRLoader( scope.manager ); 32 | loader.setCrossOrigin( this.crossOrigin ); 33 | loader.load( url, function ( text ) { 34 | 35 | scope.parse( JSON.parse( text ), onLoad, url ); 36 | 37 | }, onProgress, onError ); 38 | 39 | }, 40 | 41 | setCrossOrigin: function ( value ) { 42 | 43 | this.crossOrigin = value; 44 | 45 | }, 46 | 47 | addGeometryHandler: function ( typeID, loaderClass ) { 48 | 49 | this.geometryHandlers[ typeID ] = { "loaderClass": loaderClass }; 50 | 51 | }, 52 | 53 | addHierarchyHandler: function ( typeID, loaderClass ) { 54 | 55 | this.hierarchyHandlers[ typeID ] = { "loaderClass": loaderClass }; 56 | 57 | }, 58 | 59 | parse: function ( json, callbackFinished, url ) { 60 | 61 | var scope = this; 62 | 63 | var urlBase = THREE.Loader.prototype.extractUrlBase( url ); 64 | 65 | var geometry, material, camera, fog, 66 | texture, images, color, 67 | light, hex, intensity, 68 | counter_models, counter_textures, 69 | total_models, total_textures, 70 | result; 71 | 72 | var target_array = []; 73 | 74 | var data = json; 75 | 76 | // async geometry loaders 77 | 78 | for ( var typeID in this.geometryHandlers ) { 79 | 80 | var loaderClass = this.geometryHandlers[ typeID ][ "loaderClass" ]; 81 | this.geometryHandlers[ typeID ][ "loaderObject" ] = new loaderClass(); 82 | 83 | } 84 | 85 | // async hierachy loaders 86 | 87 | for ( var typeID in this.hierarchyHandlers ) { 88 | 89 | var loaderClass = this.hierarchyHandlers[ typeID ][ "loaderClass" ]; 90 | this.hierarchyHandlers[ typeID ][ "loaderObject" ] = new loaderClass(); 91 | 92 | } 93 | 94 | counter_models = 0; 95 | counter_textures = 0; 96 | 97 | result = { 98 | 99 | scene: new THREE.Scene(), 100 | geometries: {}, 101 | face_materials: {}, 102 | materials: {}, 103 | textures: {}, 104 | objects: {}, 105 | cameras: {}, 106 | lights: {}, 107 | fogs: {}, 108 | empties: {}, 109 | groups: {} 110 | 111 | }; 112 | 113 | if ( data.transform ) { 114 | 115 | var position = data.transform.position, 116 | rotation = data.transform.rotation, 117 | scale = data.transform.scale; 118 | 119 | if ( position ) { 120 | 121 | result.scene.position.fromArray( position ); 122 | 123 | } 124 | 125 | if ( rotation ) { 126 | 127 | result.scene.rotation.fromArray( rotation ); 128 | 129 | } 130 | 131 | if ( scale ) { 132 | 133 | result.scene.scale.fromArray( scale ); 134 | 135 | } 136 | 137 | if ( position || rotation || scale ) { 138 | 139 | result.scene.updateMatrix(); 140 | result.scene.updateMatrixWorld(); 141 | 142 | } 143 | 144 | } 145 | 146 | function get_url( source_url, url_type ) { 147 | 148 | if ( url_type == "relativeToHTML" ) { 149 | 150 | return source_url; 151 | 152 | } else { 153 | 154 | return urlBase + source_url; 155 | 156 | } 157 | 158 | }; 159 | 160 | // toplevel loader function, delegates to handle_children 161 | 162 | function handle_objects() { 163 | 164 | handle_children( result.scene, data.objects ); 165 | 166 | } 167 | 168 | // handle all the children from the loaded json and attach them to given parent 169 | 170 | function handle_children( parent, children ) { 171 | 172 | var mat, dst, pos, rot, scl, quat; 173 | 174 | for ( var objID in children ) { 175 | 176 | // check by id if child has already been handled, 177 | // if not, create new object 178 | 179 | var object = result.objects[ objID ]; 180 | var objJSON = children[ objID ]; 181 | 182 | if ( object === undefined ) { 183 | 184 | // meshes 185 | 186 | if ( objJSON.type && ( objJSON.type in scope.hierarchyHandlers ) ) { 187 | 188 | if ( objJSON.loading === undefined ) { 189 | 190 | var reservedTypes = { 191 | "type": 1, "url": 1, "material": 1, 192 | "position": 1, "rotation": 1, "scale" : 1, 193 | "visible": 1, "children": 1, "userData": 1, 194 | "skin": 1, "morph": 1, "mirroredLoop": 1, "duration": 1 195 | }; 196 | 197 | var loaderParameters = {}; 198 | 199 | for ( var parType in objJSON ) { 200 | 201 | if ( ! ( parType in reservedTypes ) ) { 202 | 203 | loaderParameters[ parType ] = objJSON[ parType ]; 204 | 205 | } 206 | 207 | } 208 | 209 | material = result.materials[ objJSON.material ]; 210 | 211 | objJSON.loading = true; 212 | 213 | var loader = scope.hierarchyHandlers[ objJSON.type ][ "loaderObject" ]; 214 | 215 | // ColladaLoader 216 | 217 | if ( loader.options ) { 218 | 219 | loader.load( get_url( objJSON.url, data.urlBaseType ), create_callback_hierachy( objID, parent, material, objJSON ) ); 220 | 221 | // UTF8Loader 222 | // OBJLoader 223 | 224 | } else { 225 | 226 | loader.load( get_url( objJSON.url, data.urlBaseType ), create_callback_hierachy( objID, parent, material, objJSON ), loaderParameters ); 227 | 228 | } 229 | 230 | } 231 | 232 | } else if ( objJSON.geometry !== undefined ) { 233 | 234 | geometry = result.geometries[ objJSON.geometry ]; 235 | 236 | // geometry already loaded 237 | 238 | if ( geometry ) { 239 | 240 | var needsTangents = false; 241 | 242 | material = result.materials[ objJSON.material ]; 243 | needsTangents = material instanceof THREE.ShaderMaterial; 244 | 245 | pos = objJSON.position; 246 | rot = objJSON.rotation; 247 | scl = objJSON.scale; 248 | mat = objJSON.matrix; 249 | quat = objJSON.quaternion; 250 | 251 | // use materials from the model file 252 | // if there is no material specified in the object 253 | 254 | if ( ! objJSON.material ) { 255 | 256 | material = new THREE.MeshFaceMaterial( result.face_materials[ objJSON.geometry ] ); 257 | 258 | } 259 | 260 | // use materials from the model file 261 | // if there is just empty face material 262 | // (must create new material as each model has its own face material) 263 | 264 | if ( ( material instanceof THREE.MeshFaceMaterial ) && material.materials.length === 0 ) { 265 | 266 | material = new THREE.MeshFaceMaterial( result.face_materials[ objJSON.geometry ] ); 267 | 268 | } 269 | 270 | if ( material instanceof THREE.MeshFaceMaterial ) { 271 | 272 | for ( var i = 0; i < material.materials.length; i ++ ) { 273 | 274 | needsTangents = needsTangents || ( material.materials[ i ] instanceof THREE.ShaderMaterial ); 275 | 276 | } 277 | 278 | } 279 | 280 | if ( needsTangents ) { 281 | 282 | geometry.computeTangents(); 283 | 284 | } 285 | 286 | if ( objJSON.skin ) { 287 | 288 | object = new THREE.SkinnedMesh( geometry, material ); 289 | 290 | } else if ( objJSON.morph ) { 291 | 292 | object = new THREE.MorphAnimMesh( geometry, material ); 293 | 294 | if ( objJSON.duration !== undefined ) { 295 | 296 | object.duration = objJSON.duration; 297 | 298 | } 299 | 300 | if ( objJSON.time !== undefined ) { 301 | 302 | object.time = objJSON.time; 303 | 304 | } 305 | 306 | if ( objJSON.mirroredLoop !== undefined ) { 307 | 308 | object.mirroredLoop = objJSON.mirroredLoop; 309 | 310 | } 311 | 312 | if ( material.morphNormals ) { 313 | 314 | geometry.computeMorphNormals(); 315 | 316 | } 317 | 318 | } else { 319 | 320 | object = new THREE.Mesh( geometry, material ); 321 | 322 | } 323 | 324 | object.name = objID; 325 | 326 | if ( mat ) { 327 | 328 | object.matrixAutoUpdate = false; 329 | object.matrix.set( 330 | mat[0], mat[1], mat[2], mat[3], 331 | mat[4], mat[5], mat[6], mat[7], 332 | mat[8], mat[9], mat[10], mat[11], 333 | mat[12], mat[13], mat[14], mat[15] 334 | ); 335 | 336 | } else { 337 | 338 | object.position.fromArray( pos ); 339 | 340 | if ( quat ) { 341 | 342 | object.quaternion.fromArray( quat ); 343 | 344 | } else { 345 | 346 | object.rotation.fromArray( rot ); 347 | 348 | } 349 | 350 | object.scale.fromArray( scl ); 351 | 352 | } 353 | 354 | object.visible = objJSON.visible; 355 | object.castShadow = objJSON.castShadow; 356 | object.receiveShadow = objJSON.receiveShadow; 357 | 358 | parent.add( object ); 359 | 360 | result.objects[ objID ] = object; 361 | 362 | } 363 | 364 | // lights 365 | 366 | } else if ( objJSON.type === "AmbientLight" || objJSON.type === "PointLight" || 367 | objJSON.type === "DirectionalLight" || objJSON.type === "SpotLight" || 368 | objJSON.type === "HemisphereLight" || objJSON.type === "AreaLight" ) { 369 | 370 | var color = objJSON.color; 371 | var intensity = objJSON.intensity; 372 | var distance = objJSON.distance; 373 | var position = objJSON.position; 374 | var rotation = objJSON.rotation; 375 | 376 | switch ( objJSON.type ) { 377 | 378 | case 'AmbientLight': 379 | light = new THREE.AmbientLight( color ); 380 | break; 381 | 382 | case 'PointLight': 383 | light = new THREE.PointLight( color, intensity, distance ); 384 | light.position.fromArray( position ); 385 | break; 386 | 387 | case 'DirectionalLight': 388 | light = new THREE.DirectionalLight( color, intensity ); 389 | light.position.fromArray( objJSON.direction ); 390 | break; 391 | 392 | case 'SpotLight': 393 | light = new THREE.SpotLight( color, intensity, distance, 1 ); 394 | light.angle = objJSON.angle; 395 | light.position.fromArray( position ); 396 | light.target.set( position[ 0 ], position[ 1 ] - distance, position[ 2 ] ); 397 | light.target.applyEuler( new THREE.Euler( rotation[ 0 ], rotation[ 1 ], rotation[ 2 ], 'XYZ' ) ); 398 | break; 399 | 400 | case 'HemisphereLight': 401 | light = new THREE.DirectionalLight( color, intensity, distance ); 402 | light.target.set( position[ 0 ], position[ 1 ] - distance, position[ 2 ] ); 403 | light.target.applyEuler( new THREE.Euler( rotation[ 0 ], rotation[ 1 ], rotation[ 2 ], 'XYZ' ) ); 404 | break; 405 | 406 | case 'AreaLight': 407 | light = new THREE.AreaLight(color, intensity); 408 | light.position.fromArray( position ); 409 | light.width = objJSON.size; 410 | light.height = objJSON.size_y; 411 | break; 412 | 413 | } 414 | 415 | parent.add( light ); 416 | 417 | light.name = objID; 418 | result.lights[ objID ] = light; 419 | result.objects[ objID ] = light; 420 | 421 | // cameras 422 | 423 | } else if ( objJSON.type === "PerspectiveCamera" || objJSON.type === "OrthographicCamera" ) { 424 | 425 | pos = objJSON.position; 426 | rot = objJSON.rotation; 427 | quat = objJSON.quaternion; 428 | 429 | if ( objJSON.type === "PerspectiveCamera" ) { 430 | 431 | camera = new THREE.PerspectiveCamera( objJSON.fov, objJSON.aspect, objJSON.near, objJSON.far ); 432 | 433 | } else if ( objJSON.type === "OrthographicCamera" ) { 434 | 435 | camera = new THREE.OrthographicCamera( objJSON.left, objJSON.right, objJSON.top, objJSON.bottom, objJSON.near, objJSON.far ); 436 | 437 | } 438 | 439 | camera.name = objID; 440 | camera.position.fromArray( pos ); 441 | 442 | if ( quat !== undefined ) { 443 | 444 | camera.quaternion.fromArray( quat ); 445 | 446 | } else if ( rot !== undefined ) { 447 | 448 | camera.rotation.fromArray( rot ); 449 | 450 | } else if ( objJSON.target ) { 451 | 452 | camera.lookAt( new THREE.Vector3().fromArray( objJSON.target ) ); 453 | 454 | } 455 | 456 | parent.add( camera ); 457 | 458 | result.cameras[ objID ] = camera; 459 | result.objects[ objID ] = camera; 460 | 461 | // pure Object3D 462 | 463 | } else { 464 | 465 | pos = objJSON.position; 466 | rot = objJSON.rotation; 467 | scl = objJSON.scale; 468 | quat = objJSON.quaternion; 469 | 470 | object = new THREE.Object3D(); 471 | object.name = objID; 472 | object.position.fromArray( pos ); 473 | 474 | if ( quat ) { 475 | 476 | object.quaternion.fromArray( quat ); 477 | 478 | } else { 479 | 480 | object.rotation.fromArray( rot ); 481 | 482 | } 483 | 484 | object.scale.fromArray( scl ); 485 | object.visible = ( objJSON.visible !== undefined ) ? objJSON.visible : false; 486 | 487 | parent.add( object ); 488 | 489 | result.objects[ objID ] = object; 490 | result.empties[ objID ] = object; 491 | 492 | } 493 | 494 | if ( object ) { 495 | 496 | if ( objJSON.userData !== undefined ) { 497 | 498 | for ( var key in objJSON.userData ) { 499 | 500 | var value = objJSON.userData[ key ]; 501 | object.userData[ key ] = value; 502 | 503 | } 504 | 505 | } 506 | 507 | if ( objJSON.groups !== undefined ) { 508 | 509 | for ( var i = 0; i < objJSON.groups.length; i ++ ) { 510 | 511 | var groupID = objJSON.groups[ i ]; 512 | 513 | if ( result.groups[ groupID ] === undefined ) { 514 | 515 | result.groups[ groupID ] = []; 516 | 517 | } 518 | 519 | result.groups[ groupID ].push( objID ); 520 | 521 | } 522 | 523 | } 524 | 525 | } 526 | 527 | } 528 | 529 | if ( object !== undefined && objJSON.children !== undefined ) { 530 | 531 | handle_children( object, objJSON.children ); 532 | 533 | } 534 | 535 | } 536 | 537 | }; 538 | 539 | function handle_mesh( geo, mat, id ) { 540 | 541 | result.geometries[ id ] = geo; 542 | result.face_materials[ id ] = mat; 543 | handle_objects(); 544 | 545 | }; 546 | 547 | function handle_hierarchy( node, id, parent, material, obj ) { 548 | 549 | var p = obj.position; 550 | var r = obj.rotation; 551 | var q = obj.quaternion; 552 | var s = obj.scale; 553 | 554 | node.position.fromArray( p ); 555 | 556 | if ( q ) { 557 | 558 | node.quaternion.fromArray( q ); 559 | 560 | } else { 561 | 562 | node.rotation.fromArray( r ); 563 | 564 | } 565 | 566 | node.scale.fromArray( s ); 567 | 568 | // override children materials 569 | // if object material was specified in JSON explicitly 570 | 571 | if ( material ) { 572 | 573 | node.traverse( function ( child ) { 574 | 575 | child.material = material; 576 | 577 | } ); 578 | 579 | } 580 | 581 | // override children visibility 582 | // with root node visibility as specified in JSON 583 | 584 | var visible = ( obj.visible !== undefined ) ? obj.visible : true; 585 | 586 | node.traverse( function ( child ) { 587 | 588 | child.visible = visible; 589 | 590 | } ); 591 | 592 | parent.add( node ); 593 | 594 | node.name = id; 595 | 596 | result.objects[ id ] = node; 597 | handle_objects(); 598 | 599 | }; 600 | 601 | function create_callback_geometry( id ) { 602 | 603 | return function ( geo, mat ) { 604 | 605 | geo.name = id; 606 | 607 | handle_mesh( geo, mat, id ); 608 | 609 | counter_models -= 1; 610 | 611 | scope.onLoadComplete(); 612 | 613 | async_callback_gate(); 614 | 615 | } 616 | 617 | }; 618 | 619 | function create_callback_hierachy( id, parent, material, obj ) { 620 | 621 | return function ( event ) { 622 | 623 | var result; 624 | 625 | // loaders which use EventDispatcher 626 | 627 | if ( event.content ) { 628 | 629 | result = event.content; 630 | 631 | // ColladaLoader 632 | 633 | } else if ( event.dae ) { 634 | 635 | result = event.scene; 636 | 637 | 638 | // UTF8Loader 639 | 640 | } else { 641 | 642 | result = event; 643 | 644 | } 645 | 646 | handle_hierarchy( result, id, parent, material, obj ); 647 | 648 | counter_models -= 1; 649 | 650 | scope.onLoadComplete(); 651 | 652 | async_callback_gate(); 653 | 654 | } 655 | 656 | }; 657 | 658 | function create_callback_embed( id ) { 659 | 660 | return function ( geo, mat ) { 661 | 662 | geo.name = id; 663 | 664 | result.geometries[ id ] = geo; 665 | result.face_materials[ id ] = mat; 666 | 667 | } 668 | 669 | }; 670 | 671 | function async_callback_gate() { 672 | 673 | var progress = { 674 | 675 | totalModels : total_models, 676 | totalTextures : total_textures, 677 | loadedModels : total_models - counter_models, 678 | loadedTextures : total_textures - counter_textures 679 | 680 | }; 681 | 682 | scope.callbackProgress( progress, result ); 683 | 684 | scope.onLoadProgress(); 685 | 686 | if ( counter_models === 0 && counter_textures === 0 ) { 687 | 688 | finalize(); 689 | callbackFinished( result ); 690 | 691 | } 692 | 693 | }; 694 | 695 | function finalize() { 696 | 697 | // take care of targets which could be asynchronously loaded objects 698 | 699 | for ( var i = 0; i < target_array.length; i ++ ) { 700 | 701 | var ta = target_array[ i ]; 702 | 703 | var target = result.objects[ ta.targetName ]; 704 | 705 | if ( target ) { 706 | 707 | ta.object.target = target; 708 | 709 | } else { 710 | 711 | // if there was error and target of specified name doesn't exist in the scene file 712 | // create instead dummy target 713 | // (target must be added to scene explicitly as parent is already added) 714 | 715 | ta.object.target = new THREE.Object3D(); 716 | result.scene.add( ta.object.target ); 717 | 718 | } 719 | 720 | ta.object.target.userData.targetInverse = ta.object; 721 | 722 | } 723 | 724 | }; 725 | 726 | var callbackTexture = function ( count ) { 727 | 728 | counter_textures -= count; 729 | async_callback_gate(); 730 | 731 | scope.onLoadComplete(); 732 | 733 | }; 734 | 735 | // must use this instead of just directly calling callbackTexture 736 | // because of closure in the calling context loop 737 | 738 | var generateTextureCallback = function ( count ) { 739 | 740 | return function () { 741 | 742 | callbackTexture( count ); 743 | 744 | }; 745 | 746 | }; 747 | 748 | function traverse_json_hierarchy( objJSON, callback ) { 749 | 750 | callback( objJSON ); 751 | 752 | if ( objJSON.children !== undefined ) { 753 | 754 | for ( var objChildID in objJSON.children ) { 755 | 756 | traverse_json_hierarchy( objJSON.children[ objChildID ], callback ); 757 | 758 | } 759 | 760 | } 761 | 762 | }; 763 | 764 | // first go synchronous elements 765 | 766 | // fogs 767 | 768 | var fogID, fogJSON; 769 | 770 | for ( fogID in data.fogs ) { 771 | 772 | fogJSON = data.fogs[ fogID ]; 773 | 774 | if ( fogJSON.type === "linear" ) { 775 | 776 | fog = new THREE.Fog( 0x000000, fogJSON.near, fogJSON.far ); 777 | 778 | } else if ( fogJSON.type === "exp2" ) { 779 | 780 | fog = new THREE.FogExp2( 0x000000, fogJSON.density ); 781 | 782 | } 783 | 784 | color = fogJSON.color; 785 | fog.color.setRGB( color[0], color[1], color[2] ); 786 | 787 | result.fogs[ fogID ] = fog; 788 | 789 | } 790 | 791 | // now come potentially asynchronous elements 792 | 793 | // geometries 794 | 795 | // count how many geometries will be loaded asynchronously 796 | 797 | var geoID, geoJSON; 798 | 799 | for ( geoID in data.geometries ) { 800 | 801 | geoJSON = data.geometries[ geoID ]; 802 | 803 | if ( geoJSON.type in this.geometryHandlers ) { 804 | 805 | counter_models += 1; 806 | 807 | scope.onLoadStart(); 808 | 809 | } 810 | 811 | } 812 | 813 | // count how many hierarchies will be loaded asynchronously 814 | 815 | for ( var objID in data.objects ) { 816 | 817 | traverse_json_hierarchy( data.objects[ objID ], function ( objJSON ) { 818 | 819 | if ( objJSON.type && ( objJSON.type in scope.hierarchyHandlers ) ) { 820 | 821 | counter_models += 1; 822 | 823 | scope.onLoadStart(); 824 | 825 | } 826 | 827 | }); 828 | 829 | } 830 | 831 | total_models = counter_models; 832 | 833 | for ( geoID in data.geometries ) { 834 | 835 | geoJSON = data.geometries[ geoID ]; 836 | 837 | if ( geoJSON.type === "cube" ) { 838 | 839 | geometry = new THREE.BoxGeometry( geoJSON.width, geoJSON.height, geoJSON.depth, geoJSON.widthSegments, geoJSON.heightSegments, geoJSON.depthSegments ); 840 | geometry.name = geoID; 841 | result.geometries[ geoID ] = geometry; 842 | 843 | } else if ( geoJSON.type === "plane" ) { 844 | 845 | geometry = new THREE.PlaneGeometry( geoJSON.width, geoJSON.height, geoJSON.widthSegments, geoJSON.heightSegments ); 846 | geometry.name = geoID; 847 | result.geometries[ geoID ] = geometry; 848 | 849 | } else if ( geoJSON.type === "sphere" ) { 850 | 851 | geometry = new THREE.SphereGeometry( geoJSON.radius, geoJSON.widthSegments, geoJSON.heightSegments ); 852 | geometry.name = geoID; 853 | result.geometries[ geoID ] = geometry; 854 | 855 | } else if ( geoJSON.type === "cylinder" ) { 856 | 857 | geometry = new THREE.CylinderGeometry( geoJSON.topRad, geoJSON.botRad, geoJSON.height, geoJSON.radSegs, geoJSON.heightSegs ); 858 | geometry.name = geoID; 859 | result.geometries[ geoID ] = geometry; 860 | 861 | } else if ( geoJSON.type === "torus" ) { 862 | 863 | geometry = new THREE.TorusGeometry( geoJSON.radius, geoJSON.tube, geoJSON.segmentsR, geoJSON.segmentsT ); 864 | geometry.name = geoID; 865 | result.geometries[ geoID ] = geometry; 866 | 867 | } else if ( geoJSON.type === "icosahedron" ) { 868 | 869 | geometry = new THREE.IcosahedronGeometry( geoJSON.radius, geoJSON.subdivisions ); 870 | geometry.name = geoID; 871 | result.geometries[ geoID ] = geometry; 872 | 873 | } else if ( geoJSON.type in this.geometryHandlers ) { 874 | 875 | var loaderParameters = {}; 876 | 877 | for ( var parType in geoJSON ) { 878 | 879 | if ( parType !== "type" && parType !== "url" ) { 880 | 881 | loaderParameters[ parType ] = geoJSON[ parType ]; 882 | 883 | } 884 | 885 | } 886 | 887 | var loader = this.geometryHandlers[ geoJSON.type ][ "loaderObject" ]; 888 | loader.load( get_url( geoJSON.url, data.urlBaseType ), create_callback_geometry( geoID ), loaderParameters ); 889 | 890 | } else if ( geoJSON.type === "embedded" ) { 891 | 892 | var modelJson = data.embeds[ geoJSON.id ], 893 | texture_path = ""; 894 | 895 | // pass metadata along to jsonLoader so it knows the format version 896 | 897 | modelJson.metadata = data.metadata; 898 | 899 | if ( modelJson ) { 900 | 901 | var jsonLoader = this.geometryHandlers[ "ascii" ][ "loaderObject" ]; 902 | var model = jsonLoader.parse( modelJson, texture_path ); 903 | create_callback_embed( geoID )( model.geometry, model.materials ); 904 | 905 | } 906 | 907 | } 908 | 909 | } 910 | 911 | // textures 912 | 913 | // count how many textures will be loaded asynchronously 914 | 915 | var textureID, textureJSON; 916 | 917 | for ( textureID in data.textures ) { 918 | 919 | textureJSON = data.textures[ textureID ]; 920 | 921 | if ( textureJSON.url instanceof Array ) { 922 | 923 | counter_textures += textureJSON.url.length; 924 | 925 | for( var n = 0; n < textureJSON.url.length; n ++ ) { 926 | 927 | scope.onLoadStart(); 928 | 929 | } 930 | 931 | } else { 932 | 933 | counter_textures += 1; 934 | 935 | scope.onLoadStart(); 936 | 937 | } 938 | 939 | } 940 | 941 | total_textures = counter_textures; 942 | 943 | for ( textureID in data.textures ) { 944 | 945 | textureJSON = data.textures[ textureID ]; 946 | 947 | if ( textureJSON.mapping !== undefined && THREE[ textureJSON.mapping ] !== undefined ) { 948 | 949 | textureJSON.mapping = new THREE[ textureJSON.mapping ](); 950 | 951 | } 952 | 953 | var texture; 954 | 955 | if ( textureJSON.url instanceof Array ) { 956 | 957 | var count = textureJSON.url.length; 958 | var url_array = []; 959 | 960 | for ( var i = 0; i < count; i ++ ) { 961 | 962 | url_array[ i ] = get_url( textureJSON.url[ i ], data.urlBaseType ); 963 | 964 | } 965 | 966 | var loader = THREE.Loader.Handlers.get( url_array[ 0 ] ); 967 | 968 | if ( loader !== null ) { 969 | 970 | texture = loader.load( url_array, generateTextureCallback( count ) ); 971 | texture.mapping = textureJSON.mapping; 972 | 973 | } else { 974 | 975 | texture = THREE.ImageUtils.loadTextureCube( url_array, textureJSON.mapping, generateTextureCallback( count ) ); 976 | 977 | } 978 | 979 | } else { 980 | 981 | var fullUrl = get_url( textureJSON.url, data.urlBaseType ); 982 | var textureCallback = generateTextureCallback( 1 ); 983 | 984 | var loader = THREE.Loader.Handlers.get( fullUrl ); 985 | 986 | if ( loader !== null ) { 987 | 988 | texture = loader.load( fullUrl, textureCallback ); 989 | 990 | } else { 991 | 992 | texture = new THREE.Texture(); 993 | loader = new THREE.ImageLoader(); 994 | 995 | ( function ( texture ) { 996 | 997 | loader.load( fullUrl, function ( image ) { 998 | 999 | texture.image = image; 1000 | texture.needsUpdate = true; 1001 | 1002 | textureCallback(); 1003 | 1004 | } ); 1005 | 1006 | } )( texture ) 1007 | 1008 | 1009 | } 1010 | 1011 | texture.mapping = textureJSON.mapping; 1012 | 1013 | if ( THREE[ textureJSON.minFilter ] !== undefined ) 1014 | texture.minFilter = THREE[ textureJSON.minFilter ]; 1015 | 1016 | if ( THREE[ textureJSON.magFilter ] !== undefined ) 1017 | texture.magFilter = THREE[ textureJSON.magFilter ]; 1018 | 1019 | if ( textureJSON.anisotropy ) texture.anisotropy = textureJSON.anisotropy; 1020 | 1021 | if ( textureJSON.repeat ) { 1022 | 1023 | texture.repeat.set( textureJSON.repeat[ 0 ], textureJSON.repeat[ 1 ] ); 1024 | 1025 | if ( textureJSON.repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping; 1026 | if ( textureJSON.repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping; 1027 | 1028 | } 1029 | 1030 | if ( textureJSON.offset ) { 1031 | 1032 | texture.offset.set( textureJSON.offset[ 0 ], textureJSON.offset[ 1 ] ); 1033 | 1034 | } 1035 | 1036 | // handle wrap after repeat so that default repeat can be overriden 1037 | 1038 | if ( textureJSON.wrap ) { 1039 | 1040 | var wrapMap = { 1041 | "repeat": THREE.RepeatWrapping, 1042 | "mirror": THREE.MirroredRepeatWrapping 1043 | } 1044 | 1045 | if ( wrapMap[ textureJSON.wrap[ 0 ] ] !== undefined ) texture.wrapS = wrapMap[ textureJSON.wrap[ 0 ] ]; 1046 | if ( wrapMap[ textureJSON.wrap[ 1 ] ] !== undefined ) texture.wrapT = wrapMap[ textureJSON.wrap[ 1 ] ]; 1047 | 1048 | } 1049 | 1050 | } 1051 | 1052 | result.textures[ textureID ] = texture; 1053 | 1054 | } 1055 | 1056 | // materials 1057 | 1058 | var matID, matJSON; 1059 | var parID; 1060 | 1061 | for ( matID in data.materials ) { 1062 | 1063 | matJSON = data.materials[ matID ]; 1064 | 1065 | for ( parID in matJSON.parameters ) { 1066 | 1067 | if ( parID === "envMap" || parID === "map" || parID === "lightMap" || parID === "bumpMap" || parID === "alphaMap" ) { 1068 | 1069 | matJSON.parameters[ parID ] = result.textures[ matJSON.parameters[ parID ] ]; 1070 | 1071 | } else if ( parID === "shading" ) { 1072 | 1073 | matJSON.parameters[ parID ] = ( matJSON.parameters[ parID ] === "flat" ) ? THREE.FlatShading : THREE.SmoothShading; 1074 | 1075 | } else if ( parID === "side" ) { 1076 | 1077 | if ( matJSON.parameters[ parID ] == "double" ) { 1078 | 1079 | matJSON.parameters[ parID ] = THREE.DoubleSide; 1080 | 1081 | } else if ( matJSON.parameters[ parID ] == "back" ) { 1082 | 1083 | matJSON.parameters[ parID ] = THREE.BackSide; 1084 | 1085 | } else { 1086 | 1087 | matJSON.parameters[ parID ] = THREE.FrontSide; 1088 | 1089 | } 1090 | 1091 | } else if ( parID === "blending" ) { 1092 | 1093 | matJSON.parameters[ parID ] = matJSON.parameters[ parID ] in THREE ? THREE[ matJSON.parameters[ parID ] ] : THREE.NormalBlending; 1094 | 1095 | } else if ( parID === "combine" ) { 1096 | 1097 | matJSON.parameters[ parID ] = matJSON.parameters[ parID ] in THREE ? THREE[ matJSON.parameters[ parID ] ] : THREE.MultiplyOperation; 1098 | 1099 | } else if ( parID === "vertexColors" ) { 1100 | 1101 | if ( matJSON.parameters[ parID ] == "face" ) { 1102 | 1103 | matJSON.parameters[ parID ] = THREE.FaceColors; 1104 | 1105 | // default to vertex colors if "vertexColors" is anything else face colors or 0 / null / false 1106 | 1107 | } else if ( matJSON.parameters[ parID ] ) { 1108 | 1109 | matJSON.parameters[ parID ] = THREE.VertexColors; 1110 | 1111 | } 1112 | 1113 | } else if ( parID === "wrapRGB" ) { 1114 | 1115 | var v3 = matJSON.parameters[ parID ]; 1116 | matJSON.parameters[ parID ] = new THREE.Vector3( v3[ 0 ], v3[ 1 ], v3[ 2 ] ); 1117 | 1118 | } 1119 | 1120 | } 1121 | 1122 | if ( matJSON.parameters.opacity !== undefined && matJSON.parameters.opacity < 1.0 ) { 1123 | 1124 | matJSON.parameters.transparent = true; 1125 | 1126 | } 1127 | 1128 | if ( matJSON.parameters.normalMap ) { 1129 | 1130 | var shader = THREE.ShaderLib[ "normalmap" ]; 1131 | var uniforms = THREE.UniformsUtils.clone( shader.uniforms ); 1132 | 1133 | var diffuse = matJSON.parameters.color; 1134 | var specular = matJSON.parameters.specular; 1135 | var ambient = matJSON.parameters.ambient; 1136 | var shininess = matJSON.parameters.shininess; 1137 | 1138 | uniforms[ "tNormal" ].value = result.textures[ matJSON.parameters.normalMap ]; 1139 | 1140 | if ( matJSON.parameters.normalScale ) { 1141 | 1142 | uniforms[ "uNormalScale" ].value.set( matJSON.parameters.normalScale[ 0 ], matJSON.parameters.normalScale[ 1 ] ); 1143 | 1144 | } 1145 | 1146 | if ( matJSON.parameters.map ) { 1147 | 1148 | uniforms[ "tDiffuse" ].value = matJSON.parameters.map; 1149 | uniforms[ "enableDiffuse" ].value = true; 1150 | 1151 | } 1152 | 1153 | if ( matJSON.parameters.envMap ) { 1154 | 1155 | uniforms[ "tCube" ].value = matJSON.parameters.envMap; 1156 | uniforms[ "enableReflection" ].value = true; 1157 | uniforms[ "reflectivity" ].value = matJSON.parameters.reflectivity; 1158 | 1159 | } 1160 | 1161 | if ( matJSON.parameters.lightMap ) { 1162 | 1163 | uniforms[ "tAO" ].value = matJSON.parameters.lightMap; 1164 | uniforms[ "enableAO" ].value = true; 1165 | 1166 | } 1167 | 1168 | if ( matJSON.parameters.specularMap ) { 1169 | 1170 | uniforms[ "tSpecular" ].value = result.textures[ matJSON.parameters.specularMap ]; 1171 | uniforms[ "enableSpecular" ].value = true; 1172 | 1173 | } 1174 | 1175 | if ( matJSON.parameters.displacementMap ) { 1176 | 1177 | uniforms[ "tDisplacement" ].value = result.textures[ matJSON.parameters.displacementMap ]; 1178 | uniforms[ "enableDisplacement" ].value = true; 1179 | 1180 | uniforms[ "uDisplacementBias" ].value = matJSON.parameters.displacementBias; 1181 | uniforms[ "uDisplacementScale" ].value = matJSON.parameters.displacementScale; 1182 | 1183 | } 1184 | 1185 | uniforms[ "diffuse" ].value.setHex( diffuse ); 1186 | uniforms[ "specular" ].value.setHex( specular ); 1187 | uniforms[ "ambient" ].value.setHex( ambient ); 1188 | 1189 | uniforms[ "shininess" ].value = shininess; 1190 | 1191 | if ( matJSON.parameters.opacity ) { 1192 | 1193 | uniforms[ "opacity" ].value = matJSON.parameters.opacity; 1194 | 1195 | } 1196 | 1197 | var parameters = { fragmentShader: shader.fragmentShader, vertexShader: shader.vertexShader, uniforms: uniforms, lights: true, fog: true }; 1198 | 1199 | material = new THREE.ShaderMaterial( parameters ); 1200 | 1201 | } else { 1202 | 1203 | material = new THREE[ matJSON.type ]( matJSON.parameters ); 1204 | 1205 | } 1206 | 1207 | material.name = matID; 1208 | 1209 | result.materials[ matID ] = material; 1210 | 1211 | } 1212 | 1213 | // second pass through all materials to initialize MeshFaceMaterials 1214 | // that could be referring to other materials out of order 1215 | 1216 | for ( matID in data.materials ) { 1217 | 1218 | matJSON = data.materials[ matID ]; 1219 | 1220 | if ( matJSON.parameters.materials ) { 1221 | 1222 | var materialArray = []; 1223 | 1224 | for ( var i = 0; i < matJSON.parameters.materials.length; i ++ ) { 1225 | 1226 | var label = matJSON.parameters.materials[ i ]; 1227 | materialArray.push( result.materials[ label ] ); 1228 | 1229 | } 1230 | 1231 | result.materials[ matID ].materials = materialArray; 1232 | 1233 | } 1234 | 1235 | } 1236 | 1237 | // objects ( synchronous init of procedural primitives ) 1238 | 1239 | handle_objects(); 1240 | 1241 | // defaults 1242 | 1243 | if ( result.cameras && data.defaults.camera ) { 1244 | 1245 | result.currentCamera = result.cameras[ data.defaults.camera ]; 1246 | 1247 | } 1248 | 1249 | if ( result.fogs && data.defaults.fog ) { 1250 | 1251 | result.scene.fog = result.fogs[ data.defaults.fog ]; 1252 | 1253 | } 1254 | 1255 | // synchronous callback 1256 | 1257 | scope.callbackSync( result ); 1258 | 1259 | // just in case there are no async elements 1260 | 1261 | async_callback_gate(); 1262 | 1263 | } 1264 | 1265 | } 1266 | -------------------------------------------------------------------------------- /static/js/app.js: -------------------------------------------------------------------------------- 1 | var camera, scene, renderer; 2 | var geometry, material, mesh; 3 | var controls; 4 | 5 | var objects = []; 6 | 7 | var raycaster; 8 | 9 | var blocker = document.getElementById( 'blocker' ); 10 | var instructions = document.getElementById( 'instructions' ); 11 | 12 | // http://www.html5rocks.com/en/tutorials/pointerlock/intro/ 13 | 14 | var havePointerLock = 'pointerLockElement' in document || 'mozPointerLockElement' in document || 'webkitPointerLockElement' in document; 15 | 16 | if ( havePointerLock ) { 17 | 18 | var element = document.body; 19 | 20 | var pointerlockchange = function ( event ) { 21 | 22 | if ( document.pointerLockElement === element || document.mozPointerLockElement === element || document.webkitPointerLockElement === element ) { 23 | 24 | controls.enabled = true; 25 | 26 | blocker.style.display = 'none'; 27 | 28 | } else { 29 | 30 | controls.enabled = false; 31 | 32 | blocker.style.display = '-webkit-box'; 33 | blocker.style.display = '-moz-box'; 34 | blocker.style.display = 'box'; 35 | 36 | instructions.style.display = ''; 37 | 38 | } 39 | 40 | } 41 | 42 | var pointerlockerror = function ( event ) { 43 | 44 | instructions.style.display = ''; 45 | 46 | } 47 | 48 | // Hook pointer lock state change events 49 | document.addEventListener( 'pointerlockchange', pointerlockchange, false ); 50 | document.addEventListener( 'mozpointerlockchange', pointerlockchange, false ); 51 | document.addEventListener( 'webkitpointerlockchange', pointerlockchange, false ); 52 | 53 | document.addEventListener( 'pointerlockerror', pointerlockerror, false ); 54 | document.addEventListener( 'mozpointerlockerror', pointerlockerror, false ); 55 | document.addEventListener( 'webkitpointerlockerror', pointerlockerror, false ); 56 | 57 | instructions.addEventListener( 'click', function ( event ) { 58 | 59 | instructions.style.display = 'none'; 60 | 61 | // Ask the browser to lock the pointer 62 | element.requestPointerLock = element.requestPointerLock || element.mozRequestPointerLock || element.webkitRequestPointerLock; 63 | 64 | if ( /Firefox/i.test( navigator.userAgent ) ) { 65 | 66 | var fullscreenchange = function ( event ) { 67 | 68 | if ( document.fullscreenElement === element || document.mozFullscreenElement === element || document.mozFullScreenElement === element ) { 69 | 70 | document.removeEventListener( 'fullscreenchange', fullscreenchange ); 71 | document.removeEventListener( 'mozfullscreenchange', fullscreenchange ); 72 | 73 | element.requestPointerLock(); 74 | } 75 | 76 | } 77 | 78 | document.addEventListener( 'fullscreenchange', fullscreenchange, false ); 79 | document.addEventListener( 'mozfullscreenchange', fullscreenchange, false ); 80 | 81 | element.requestFullscreen = element.requestFullscreen || element.mozRequestFullscreen || element.mozRequestFullScreen || element.webkitRequestFullscreen; 82 | 83 | //element.requestFullscreen(); 84 | element.requestPointerLock(); 85 | 86 | } else { 87 | 88 | element.requestPointerLock(); 89 | 90 | } 91 | 92 | }, false ); 93 | 94 | } else { 95 | 96 | instructions.innerHTML = 'Your browser doesn\'t seem to support Pointer Lock API'; 97 | 98 | } 99 | 100 | init(); 101 | animate(); 102 | 103 | function init() { 104 | var dae; 105 | camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1000 ); 106 | 107 | scene = new THREE.Scene(); 108 | //scene.fog = new THREE.Fog( 0xFFFFFF, 0, 1024); 109 | 110 | var light = new THREE.HemisphereLight( 0xFFFFFF, 0xFFFFFF, 1.0 ); 111 | light.position.set( 0.5, 10, 0.75 ); 112 | scene.add( light ); 113 | 114 | dirLight = new THREE.DirectionalLight( 0xffffff, 1 ); 115 | dirLight.color.setHSL( 0.1, 1, 0.95 ); 116 | dirLight.position.set( -1, 150, 1 ); 117 | dirLight.position.multiplyScalar( 50 ); 118 | scene.add( dirLight ); 119 | 120 | controls = new THREE.PointerLockControls( camera ); 121 | scene.add( controls.getObject() ); 122 | 123 | raycaster = new THREE.Raycaster( new THREE.Vector3(), new THREE.Vector3( 0, - 1, 0 ), 0, 30 ); 124 | 125 | 126 | //renderer = new THREE.WebGLRenderer({ antialias: true }); 127 | renderer = new THREE.WebGLRenderer(); 128 | renderer.setClearColor( 0xFFFFFF); 129 | renderer.setSize( window.innerWidth, window.innerHeight ); 130 | 131 | document.body.appendChild( renderer.domElement ); 132 | 133 | // 134 | 135 | window.addEventListener( 'resize', onWindowResize, false ); 136 | 137 | } 138 | 139 | function onWindowResize() { 140 | 141 | camera.aspect = window.innerWidth / window.innerHeight; 142 | camera.updateProjectionMatrix(); 143 | 144 | renderer.setSize( window.innerWidth, window.innerHeight ); 145 | 146 | } 147 | 148 | function animate() { 149 | 150 | requestAnimationFrame( animate ); 151 | 152 | controls.isOnObject( false ); 153 | 154 | raycaster.ray.origin.copy( controls.getObject().position ); 155 | raycaster.ray.origin.y -= 10; 156 | 157 | var intersections = raycaster.intersectObjects( objects ); 158 | 159 | if ( intersections.length > 0 ) { 160 | 161 | controls.isOnObject( true ); 162 | 163 | } 164 | 165 | controls.update(); 166 | 167 | renderer.render( scene, camera ); 168 | 169 | } 170 | -------------------------------------------------------------------------------- /static/js/jquery-2.1.1.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery v2.1.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */ 2 | !function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.1",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="
",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+Math.random()}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b) 3 | },_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthx",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,bb=/<([\w:]+)/,cb=/<|&#?\w+;/,db=/<(?:script|style|link)/i,eb=/checked\s*(?:[^=]|=\s*.checked.)/i,fb=/^$|\/(?:java|ecma)script/i,gb=/^true\/(.*)/,hb=/^\s*\s*$/g,ib={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ib.optgroup=ib.option,ib.tbody=ib.tfoot=ib.colgroup=ib.caption=ib.thead,ib.th=ib.td;function jb(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function kb(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function lb(a){var b=gb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function mb(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function nb(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function ob(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pb(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=ob(h),f=ob(a),d=0,e=f.length;e>d;d++)pb(f[d],g[d]);if(b)if(c)for(f=f||ob(a),g=g||ob(h),d=0,e=f.length;e>d;d++)nb(f[d],g[d]);else nb(a,h);return g=ob(h,"script"),g.length>0&&mb(g,!i&&ob(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(cb.test(e)){f=f||k.appendChild(b.createElement("div")),g=(bb.exec(e)||["",""])[1].toLowerCase(),h=ib[g]||ib._default,f.innerHTML=h[1]+e.replace(ab,"<$1>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=ob(k.appendChild(e),"script"),i&&mb(f),c)){j=0;while(e=f[j++])fb.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(ob(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&mb(ob(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(ob(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!db.test(a)&&!ib[(bb.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(ab,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(ob(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(ob(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&eb.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(ob(c,"script"),kb),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,ob(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,lb),j=0;g>j;j++)h=f[j],fb.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(hb,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qb,rb={};function sb(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function tb(a){var b=l,c=rb[a];return c||(c=sb(a,b),"none"!==c&&c||(qb=(qb||n("